Made by AI

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.

FxWater.java · 2006
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;
    }
}
fx-applets.js · 2026
// 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 + 32 reads 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 - fMaxDistance makes 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 of System.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