> Canonical page: https://www.mahieu.nl/en/applications/amiga-juggler/

- [Home](https://www.mahieu.nl/en/)
- [My applications](https://www.mahieu.nl/en/applications/)
- Amiga Juggler

My application · Real-time ray tracing

# Amiga Juggler: forty years on, every frame rendered live

In November 1986, Eric Graham had an Amiga ray-trace a juggler: 24 frames, each taking about an hour to calculate. This tribute recreates the scene live on your graphics card, with a free camera, a chunky-pixel 1986 mode and the original ‘ding’.

A personal making project.

- Status personal project, publicly available
- Technology WebGL2, GLSL and Web Audio, with no frameworks or build step; in Dutch and English

[Try the live demo](#demo) [Open the demo ↗ ](https://www.mahieu.nl/amiga-juggler/en/)

![The Amiga Juggler in 2026 mode: a figure made of pink and red spheres juggling three mirrored balls on a yellow and green chequerboard, with the camera orbiting as you scroll](https://www.mahieu.nl/assets/img/amiga-juggler/juggler-orbit-20261013-1200.webp)

[

![The Amiga Juggler at full resolution: the figure catches a mirrored ball while another hangs overhead, with a hard shadow across the chequerboard](https://www.mahieu.nl/assets/img/amiga-juggler/juggler-poster-20261013-1600.webp)

](https://www.mahieu.nl/amiga-juggler/en/)

Live ray tracing Loads when the demo comes into view [Open the demo in a separate window](https://www.mahieu.nl/amiga-juggler/en/) Start demo Take control Finish exploring

1986 2026 Fly-around Drone shot Follow the ball Bird’s-eye view Floor-level view Reset camera

[Open full screen ↗ ](https://www.mahieu.nl/amiga-juggler/en/)

The demo loads when it comes into view and starts with a click, in **1986** mode: 320 × 200 pixels in a 4:3 frame, 24 steps per loop and one ‘ding’ per catch. **2026** gives you smooth motion at full resolution. Choose **Take control** to orbit the juggler with a mouse or finger, zoom with the wheel or a pinch, and adjust the speed from 0.01× to 2× using the control at the bottom. The sound changes pitch with the speed, like a tape. Use the comma and full stop keys to step through the 24 poses; the space bar pauses. The demo toolbar hides after three seconds of inactivity and returns when you move. The demo pauses automatically when it is off screen.

![1986 mode: the juggler at 320 by 200 pixels, with chunky pixels in a 4:3 frame, matching the original Amiga frames](https://www.mahieu.nl/assets/img/amiga-juggler/juggler-1986-20261013-960.webp)

Scroll to play the loop twice: the 24 poses from 1986, each ray-traced again at 320 × 200 with 4 bits per colour channel.

The 1986 original

## An hour per frame, on an Amiga with 512 kB

Eric Graham bought an Amiga 1000 in mid-1986 and wrote a ray tracer in a single day that November. He used it to calculate 24 images of a figure juggling three mirrored balls: 320 by 200 pixels, 64,000 rays per image, and about an hour of processing per frame. The images were encoded in the Amiga’s HAM mode, displaying 4,096 colours at once, compressed losslessly to around 10 kB per frame, and played as a 0.8-second loop at 30 frames per second, with one ‘ding’ per catch.

**Commodore was initially sceptical.** The company released the animation in November 1986 to demonstrate the Amiga’s capabilities, but initially thought the images had come from a mainframe. Graham sent them his renderer so they could calculate the frames themselves. That ray tracer later became Sculpt 3D, released in autumn 1987.

**What survives.** The original camera and movement data have been lost. What remains is the set of 24 frames, the recovered Raytracer 1.0 source code from 1987 with its robot.dat scene file, and a recording from a real Amiga’s audio output. This version was reconstructed from those sources.

[Try 1986 mode](https://www.mahieu.nl/amiga-juggler/en/)

**70** spheres, ray-traced live plus an infinite chequerboard floor

**24** original frames measured balls, head, hands, feet and joints

**0.8 s** per loop, with one ‘ding’ 24 frames at 30 frames per second

**102** automated tests 82 unit tests and 20 browser tests

![The first pose in 2026 mode, using the original 1986 camera position at full resolution, with smooth spheres and sharp reflections](https://www.mahieu.nl/assets/img/amiga-juggler/juggler-vergelijk-modern-20261013-1280.webp)

![The same pose in 1986 mode: 320 by 200 pixels with 4 bits per colour channel, enlarged four times here with square pixels](https://www.mahieu.nl/assets/img/amiga-juggler/juggler-vergelijk-1986-20261013-1280.webp)

1986 2026 Drag across the image: 1986 mode on the left (320 × 200, 4 bits per channel), the same rays at full resolution on the right.

Two display modes

## The same rays, a different world of detail

**1986.** The ray tracer calculates at exactly 320 × 200, quantises each colour to 4 bits per channel (⌊16·colour + 0.5⌋, as in rt2.c), and displays the result using nearest-neighbour scaling in a 4:3 frame, with the 5:6 pixel aspect ratio of a monitor of the time. Motion steps through the original 24 discrete poses at 30 per second. One characteristic is deliberately omitted: the HAM encoder’s per-scanline colour fringing is not emulated.

**2026.** The same shader renders at your screen’s native resolution, including its device pixel ratio, with FXAA as a lightweight second pass. The 24 poses are interpolated along a closed curve for smooth movement. The vertical field of view matches the original, at approximately 50°; wider screens reveal more at the sides.

[Switch to 2026 mode](https://www.mahieu.nl/amiga-juggler/en/)

Reconstructed from 24 frames

## Camera, movement and light, measured from the source

Without the original scene and movement data, the pixels themselves are the reliable reference. Every parameter in this version is derived from them or taken from the 1987 source code.

### The camera is where it was in 1986

Fitted to the frame data: the eye is at (−6, 3.32, −6), looking at 45° across the chequerboard diagonal, almost level, with the horizon at row 97 of 200 and a focal length of 215 pixels when using square image pixels. The figure stands at the world origin.

### Movement recovered from the pixels

Ball centres, head, hands, feet and joint bend directions were measured in all 24 frames. The ball path is a Fourier fit with three harmonics per coordinate, with a reprojection error of about 2 pixels, placed on a slightly tilted 3D juggling plane. The hands move on a plane just behind it, so they meet the balls even from a freely positioned camera. Each ball completes its circuit in three loops, with the three balls one-third of a cycle apart.

### Materials and shading from the 1987 source

Seventy spheres use the materials and colours from robot.dat and the lighting model from rt1.c: wrap ambient (n·ẑ + 1.5) · 0.4 × 0.25, one distant light with cos/r², binary white highlights above cos² > 0.95, recursive mirror reflections × 0.9, hard shadows and no gamma correction. The floor is an exact port of gingham(): tiles three units wide, mirrored across both axes, including the double band at the origin visible in the original.

### A figure that also works from the side

The body follows Ben Hanke’s slimmer, camera-independent 3D pose, with fixed arm and leg lengths. Two-bone inverse kinematics keeps the hands and feet at their measured targets, so the balls do not pass through the head or torso when viewed from other angles.

### The ‘ding’ measured from a real Amiga

One ding per loop, at the catch: an almost pure 1 kHz sine wave with a stepped envelope in 50 ms intervals, lasting about 0.5 seconds. It was measured from Ernie Wright’s recording of a real Amiga’s audio output. Web Audio plays the sample like tape: at speed r, its pitch rises by r and its duration shortens to 1/r, including when the speed changes during playback.

### Every pose can be checked against the original

A verification mode renders each of the 24 poses at 320 × 200 for pixel-by-pixel comparison with the original frames. This makes the reconstruction verifiable, including after later changes.

How the GPU renders it

## Six steps from scene to pixel, dozens of times a second

-

### The scene in JavaScript

Each frame sends the 70 spheres — position, radius and material — and five bounding spheres to the GPU as uniforms. No meshes or textures are used.

-

### One ray per pixel

A GLSL ES 3.0 fragment shader casts a ray from the fitted camera position through each pixel. The whole screen is covered by one triangle.

-

### Intersections

Ray–sphere intersections are calculated analytically. The limbs are arranged in five groups, each with a bounding sphere, so most rays only need to test a few spheres. The floor is an infinite plane.

-

### Light and shadow

At each hit point, a shadow ray is cast towards the light, with hard shadows, wrap ambient and highlights following rt1.c.

-

### Reflections

The three balls reflect up to twice, with a reflection factor of 0.9, then fall back to the sky. If frame times remain high, the frame budget reduces the workload in stages: one reflection, followed by a lower rendering scale in 2026 mode. When performance improves, it tries a higher level again.

-

### Display

1986: 320 × 200 scaled to a 4:3 frame with nearest-neighbour sampling and 4 bits per channel. 2026: full resolution, with FXAA as a lightweight second pass.

### No dependencies or build step

JavaScript modules, three GLSL shaders and Web Audio are all it needs. The demo follows the same strict Content Security Policy as the rest of mahieu.nl, with no external scripts or inline code.

### Pause without a jump in time

A dedicated animation clock tracks elapsed scene time. Pausing and resuming does not cause a jump, and the 0.01× to 2× speed range applies to both visuals and sound, including during playback.

### Recovery after a lost WebGL context

If the browser loses its graphics context, for example when switching tabs on a phone, the demo rebuilds the renderer and either resumes or returns cleanly to the start screen. This also works while paused.

### Tested in a real browser

Eighty-two unit tests cover the scene, camera, clock, audio, frame budget, controls and renderer behaviour. A further 20 Playwright tests in Chromium check controls, focus, pausing, audio, full-screen display, context loss and reference images for the 24 poses. Keyboard operation, focus order and screen reader status messages are included.

[Start the drone shot](https://www.mahieu.nl/amiga-juggler/en/) [Restore the original camera](https://www.mahieu.nl/amiga-juggler/en/)

Sources and credits

## Built on what survived

This tribute is possible thanks to the people who preserved, recovered and studied the original. These are the sources behind the reconstruction.

### Eric Graham — Raytracer 1.0 (1987)

The recovered source code (rt1.c–rt3.c) and robot.dat scene file, extracted from the original floppy disk by AlphaPixel and [published on GitHub](https://github.com/AlphaPixel/Eric-Graham-1987-Juggler-Raytracer-1.0). In February 2026, Eric Graham gave permission for anyone to use the code provided he was credited. It supplies the materials, floor and shading model.

### Ernie Wright — technical analysis

The 24 original frames as PNG files, the juggler.wav recording from a real Amiga, and [the history of the demo](http://www.etwright.org/cghist/juggler.html), including Graham’s own recollections. These provide the calibration and verification material for this reconstruction.

### Meatfighter — mathematical reconstruction

A [step-by-step reconstruction](https://meatfighter.com/juggler/) deriving the camera, ball trajectories and shower juggling pattern: a high arc from left to right and a low return arc. Used as a reference for the high and low paths.

### Ben Hanke — real-time WebGL version

A [previous real-time version](http://13h.org/juggler/) of the Juggler, used as a reference for a camera-independent 3D pose.

### AmigaWorld, May/June 1987

Graham’s own article on making the Juggler. The program announced in the article became Sculpt 3D. The demo itself appeared as version 1.2 on Fish Disk 97.

Why it matters

## A measured approach, even to a juggler from 1986

**What this demonstrates.** A ray tracer no longer needs a render farm: what took an hour per image in 1986 can now be calculated dozens of times a second by a graphics card, in a browser, without a plug-in. It also shows how 24 images and some C code can become a working, tested and accessible application through careful measurement.

**How it relates to my services.** It is the same approach I bring to client work: start with what is available, build the smallest working version, measure, test and then expand. That is what [taking an idea to a proof of concept](https://www.mahieu.nl/en/proof-of-concept/) means in practice.

## Verifiable evidence

### What you can verify in Amiga Juggler

- **Problem:** The original Juggler survives as 24 HAM frames. Its camera and movement data have been lost, and the original demo is tied to the Amiga’s 1986 display.

- **Approach:** The scene, camera, lighting, movement and sound were reconstructed from the frames, recovered 1987 source code and an audio recording. They were rebuilt as an analytical ray tracer in a WebGL2 fragment shader, with a 1986 mode and a free camera.

- **Status:** Live and publicly available at [www.mahieu.nl/amiga-juggler/en/](https://www.mahieu.nl/amiga-juggler/en/), in Dutch and English. A personal project, not a commission.

- **Demonstrable result:** Seventy spheres, hard shadows and two mirror reflections calculated live per frame; the 24 original poses and the ‘ding’ reproduced; 82 unit tests and 20 browser tests passing.

- **Limitations:** WebGL2 is required. Without Web Audio, the demo runs without sound. On less powerful graphics hardware, it reduces to one reflection and a lower rendering resolution. HAM colour fringing is not emulated. Sound requires a click or tap.

- **Last reviewed:** October 2026

Frequently asked questions

## About Amiga Juggler

### What is the Amiga Juggler?

An animation created by Eric Graham in 1986: a figure juggling three mirrored balls, ray-traced on an Amiga 1000 and released by Commodore as a demonstration. It uses 24 frames in HAM mode at 320 by 200 pixels, in a 0.8-second loop with one ‘ding’.

### Is this playing the original animation?

No original frames are played back. Every frame is calculated live in your browser by your graphics card. The scene, camera and movement were reconstructed from measurements of the 24 frames and the 1987 source code.

### Why does 1986 mode look so blocky?

That was the original resolution: 320 by 200 pixels, stretched to 4:3 as on a monitor of the time, with 4 bits per colour channel and 24 steps per loop. Choose 2026 for smooth motion at full resolution.

### Does it work on a phone?

Yes, in a modern browser with WebGL2. Drag to rotate the camera, pinch to zoom and use two fingers to pan. If frame times remain high, the demo reduces the workload: first to one reflection, then to a lower rendering resolution.

### Is this permitted?

This is a tribute that uses no original images. In 2026, Eric Graham said anyone could use his code provided he was credited. Both the demo and this page credit him and the other sources.

### How does this relate to your work?

It follows the same approach I use for client projects: start with what is available, measure, build the smallest working version and test it. That is how an idea becomes a working proof of concept.

Contact

## Questions about this project or the technology behind it?

You’re welcome to get in touch.

[Get in touch](https://www.mahieu.nl/en/contact/)

Prefer to contact me directly? Call [+31 6 1400 2312](tel:+31614002312) or email [mwd@mahieu.nl](mailto:mwd@mahieu.nl).

Last updated: 2026-10-14.
