
Lab · Experiment 03
Java effects, reborn in HTML5
My old Java applets stopped working when browsers dropped Java. Here they run again: Claude ported the original source to JavaScript and Canvas, line by line, keeping the same algorithms, constants and even the bugs. The pixels are scaled up but rendered at the original 240×240.
FxStars
Star Field
2,500 stars fly towards you, growing and brightening as they get closer. After five seconds the field starts to roll, then reverses direction. The coloured fringes are original: each star pixel is written two channels apart instead of three.
FxWater
Water
A two-buffer wave equation: each point becomes half the sum of its four neighbours minus its previous value. Drag on the water to stir it. In the original, the right mouse button toggled rain and the middle button a rotating vortex. They still work, and so do the buttons below.
FxWaterPix
Water over a picture
The same water, used to shift a photo by up to 16 pixels, a cheap fake of refraction. The original drew the 160×160 picture in a corner of the applet. Here it fills the whole view. Drag over the picture. This one was in the source zip but never made it to the old page.
FxBlobs
Blobs
Seven blobs with random colour, size and speed bounce around the screen. Every pixel adds up each blob's contribution with a quadratic fall-off, so they melt together where they overlap. Their glow slowly pulses. Press Restart for a new set.
FxField
Electric Field
Three moving charges, at least one positive and one negative. Every frame, 24 field lines start from each positive charge and are traced in 2-pixel steps along the sum of the Coulomb forces, for up to 1,000 steps. Positive charges are yellow, negative ones white. Press Restart for a new configuration.
How it was ported
Same code, new runtime
The structure is the same as in 1998. A small FxBase owns the pixel buffer, the render loop and the mouse, and each effect implements the same four methods as the old FxFace interface. The Java WritableRaster.setPixels() call becomes a copy into a Canvas ImageData, and the applet thread becomes requestAnimationFrame.
for( iy = 1; iy < iY-1; iy++ )
{
iyy = iy * iX;
for( ix = 1; ix < iX-1; ix++ )
{
aa[iyy + ix] = (
ab[ iyy + ix+1 ] +
ab[ iyy + ix-1 ] +
ab[ iyy - iX + ix ] +
ab[ iyy + iX + ix ] )
/ 2.0f - aa[iyy + ix];
if( aa[iyy + ix] > 1.0f )
aa[iyy + ix] = 1.0f;
if( aa[iyy + ix] < 0.0f )
aa[iyy + ix] = 0.0f;
}
}
// Wave equation: new = (sum of 4 neighbours of previous) / 2 - older
for (iy = 1; iy < h - 1; iy++) {
var row = iy * w;
for (ix = 1; ix < w - 1; ix++) {
i = row + ix;
var v = (ab[i + 1] + ab[i - 1] +
ab[i - w] + ab[i + w]) / 2 - aa[i];
aa[i] = v > 1 ? 1 : v < 0 ? 0 : v;
}
}
Quirks kept on purpose
- Stars:
(grey + a[i]) >> 1 + 32reads like a +32 brightness boost, but Java parses it as>> 33, and shift counts wrap at 32. It's a plain average. JavaScript wraps the same way. - Stars:
fMaxDistance - fMaxDistancemakes the random start distance zero, so all stars are born exactly 160 units away. - Water: the vortex timer is never reset, so the vortex drops a point on every frame instead of every half second.
- WaterPix: the refraction offset is added to the whole pixel before masking the blue byte, so deep ripples tint the picture.
What changed
- Mouse handling uses pointer events, so dragging works with touch too. On-page buttons stand in for the middle and right mouse buttons.
- Effects pause when scrolled out of view, and show a single still frame if your system asks for reduced motion.
- Timing uses
performance.now()instead ofSystem.currentTimeMillis(). - A Restart button starts an effect again with fresh random values. In 1998 that meant reloading the applet.
- WaterPix scales its picture to fill the 240×240 view, instead of leaving it 160×160 in the top-left corner.
Read the full port (opens in a new tab) Original Java source