Eigen toepassing · Realtime raytracing

Amiga Juggler: veertig jaar later, elke frame live berekend

In november 1986 liet Eric Graham een Amiga een jongleur raytracen: 24 frames, elk ongeveer een uur rekenwerk. Deze hommage berekent dezelfde scène opnieuw, maar dan elke frame live op uw grafische kaart — met een vrije camera, een 1986-modus met dikke pixels en het originele ‘ding’.

Een persoonlijk maakproject, ontwikkeld in eigen tijd en met eigen middelen.

  • Statuseigen initiatief, publiek te bekijken
  • TechniekWebGL2, GLSL en Web Audio, zonder frameworks of buildstap; in het Nederlands en Engels
De Amiga Juggler in de moderne modus: een jongleur van roze en rode bollen met drie spiegelende ballen op een geel-groen dambord; bij het scrollen draait de camera om hem heen
De Amiga Juggler op volle resolutie: de jongleur vangt een spiegelende bal, een tweede bal hangt in de lucht boven zijn hoofd en de harde schaduw valt over het dambord
Live raytracing Laadt zodra de demo in beeld is Open de demo in een eigen venster

De demo laadt zodra hij in beeld komt en start na een klik, in de 1986-modus: 320 × 200 pixels in een 4:3-kader, 24 stappen per lus en één ‘ding’ per vangst. Modern geeft de vloeiende weergave op volle resolutie. Met Zelf besturen draait u met de muis of een vinger om de jongleur, zoomt u met het wiel of een knijpbeweging en regelt u onderaan de snelheid van 0,01× tot 2×; het geluid verandert mee in toonhoogte, zoals een bandje. Met de toetsen , en . stapt u beeld voor beeld door de 24 poses; de spatiebalk pauzeert. De demobalk verdwijnt na drie seconden rust en komt terug bij een beweging. Buiten beeld pauzeert de demo vanzelf.

De 1986-modus: de jongleur in 320 bij 200 pixels met dikke, blokkerige pixels in een 4:3-kader, precies zoals de originele Amiga-frames
Scroll en de lus speelt twee keer: de 24 poses van 1986, elk opnieuw geraytracet op 320 × 200 met 4 bits per kleurkanaal.

Het origineel, 1986

Een uur per frame, op een Amiga met 512 kB

Eric Graham kocht medio 1986 een Amiga 1000 en schreef in november van dat jaar in één dag een raytracer. Daarmee berekende hij 24 beelden van een jongleur met drie spiegelende ballen: 320 bij 200 pixels, 64.000 lichtstralen per beeld, ongeveer een uur rekenwerk per frame. De beelden werden gecodeerd in de HAM-modus van de Amiga, die 4096 kleuren tegelijk op het scherm toont, verliesvrij gecomprimeerd tot zo’n 10 kB per frame en afgespeeld als lus van 0,8 seconde op 30 beelden per seconde — met één ‘ding’ per vangst.

Commodore geloofde het eerst niet. Het bedrijf bracht de animatie in november 1986 uit als demonstratie van wat de Amiga kon, maar dacht aanvankelijk dat de beelden van een mainframe kwamen. Graham stuurde zijn renderer op, zodat ze de frames zelf konden berekenen. Uit die raytracer groeide Sculpt 3D, dat in het najaar van 1987 uitkwam.

Wat er nog is. De camera- en bewegingsdata van toen zijn verloren. Wat wél bewaard bleef: de 24 frames, de teruggevonden broncode van Raytracer 1.0 (1987) met het scènebestand robot.dat, en een opname van het geluid van een echte Amiga. Daaruit is deze versie teruggemeten.

70bollen, live geraytracetplus een oneindige dambordvloer
24frames uit 1986, nagemetenballen, hoofd, handen, voeten en gewrichten
0,8 sper lus, één ‘ding’24 frames op 30 beelden per seconde
102geautomatiseerde tests82 unittests en 20 browsertests
De eerste pose van de lus in de moderne modus: dezelfde camera als in 1986, op volle resolutie met gladde bollen en scherpe reflecties Dezelfde pose in de 1986-modus: 320 bij 200 pixels met 4 bits per kleurkanaal, hier met vierkante pixels vier keer vergroot
Schuif over het beeld: links de 1986-modus (320 × 200, 4 bits per kanaal), rechts dezelfde lichtstralen op volle resolutie.

Twee weergaven

Dezelfde lichtstralen, twee werelden van resolutie

1986. De raytracer rekent op exact 320 × 200, kwantiseert elke kleur naar 4 bits per kanaal (⌊16·kleur + 0,5⌋, zoals in rt2.c) en toont het beeld met nearest-neighbour in een 4:3-kader, met de pixelverhouding 5:6 van een monitor van toen. De beweging stapt in de 24 discrete poses van het origineel, 30 per seconde. Eén ding is bewust weggelaten: het per-scanlijn-foutgedrag van de HAM-encoder, de kleurfringing, wordt niet geëmuleerd.

Modern. Dezelfde shader rekent op de eigen resolutie van uw scherm, inclusief device-pixel-ratio, met FXAA als lichte tweede beeldpass. De 24 poses worden geïnterpoleerd als gesloten kromme, zodat de beweging vloeiend wordt. De verticale beeldhoek blijft die van het origineel (≈ 50°); brede schermen tonen meer opzij.

Teruggemeten uit 24 frames

Camera, beweging en licht: gemeten, niet geschat

Zonder de originele scène- en bewegingsdata blijft er maar één betrouwbare bron over: de pixels zelf. Elke parameter in deze versie is daaruit afgeleid of komt uit de broncode van 1987.

De camera staat waar hij in 1986 stond

Gefit op de framedata: het oog op (−6, 3,32, −6), de blik 45° over de diagonaal van het dambord, vrijwel waterpas met de horizon op rij 97 van 200, een brandpunt van 215 pixels met vierkante beeldpixels. De figuur staat op de wereldoorsprong.

De beweging komt uit de pixels

Balcentra, hoofd, handen, voeten en de buigrichting van de gewrichten zijn uit alle 24 frames gemeten. De balbaan is een Fourier-fit met drie harmonischen per coördinaat (herprojectiefout ≈ 2 pixels), gelegd op een licht gekanteld 3D-jongleervlak; de handen bewegen op een vlak er vlak achter, zodat ze de ballen ook vanuit een vrije camera echt raken. Eén bal doorloopt het circuit in drie lussen; de drie ballen liggen een derde uit fase.

Materialen en shading uit de broncode van 1987

70 bollen met de materialen en kleurwaarden uit robot.dat, en het lichtmodel van rt1.c: wrap-ambient (n·ẑ + 1,5) · 0,4 × 0,25, één verre lamp met cos/r², binaire witte glimpunten boven cos² > 0,95, spiegels als recursieve reflectie × 0,9, harde schaduwen en géén gammacorrectie. De vloer is een exacte port van gingham(): tegels van 3 eenheden, gespiegeld om beide assen — inclusief de dubbele band bij de oorsprong die in het origineel zichtbaar is.

Een figuur die ook van opzij klopt

De lichaamsbouw volgt de slankere, camera-onafhankelijke 3D-pose van Ben Hanke, met vaste arm- en beenlengtes. Two-bone IK houdt handen en voeten op hun gemeten doelen, zodat de ballen bij een vrije camera niet door hoofd of romp gaan.

Het ‘ding’ is nagemeten aan een echte Amiga

Eén ding per lus, op het vangmoment: een vrijwel zuivere sinus van 1 kHz met een trapsgewijze envelope in stappen van 50 ms (≈ 0,5 s), gemeten aan Ernie Wrights opname van de audio-uitgang van een Amiga. Web Audio speelt de sample als band af: bij snelheid r wordt de toon r× hoger en 1/r× korter, ook bij een wijziging tijdens de demo.

Elke pose naast het origineel te leggen

Een verificatiemodus rendert elk van de 24 poses 1:1 op 320 × 200, zodat ze pixel voor pixel naast de originele frames gelegd kunnen worden. Zo is de reconstructie controleerbaar, en blijft hij dat bij elke wijziging.

Zo rekent de GPU

Zes stappen van scène naar pixel, tientallen keren per seconde

  1. Scène in JavaScript

    Per frame de 70 bollen (positie, straal, materiaal) en vijf omhullende bollen, als uniforms naar de GPU. Geen meshes, geen texturen.

  2. Eén straal per pixel

    Een fragmentshader (GLSL ES 3.0) schiet vanuit het gefitte cameraoog door elke pixel één straal; het hele scherm is één driehoek.

  3. Snijpunten

    Analytische straal-bolsnijding. Ledematen zitten in vijf groepen met een omhullende bol, zodat de meeste stralen maar een paar bollen testen. De vloer is een oneindig vlak.

  4. Licht en schaduw

    Per raakpunt een schaduwstraal naar de lamp: harde schaduw, wrap-ambient en glimpunt volgens rt1.c.

  5. Spiegels

    De drie ballen weerkaatsen tot twee keer (reflectie × 0,9), daarna de lucht. Blijven frames traag, dan schakelt het framebudget trapsgewijs terug: één reflectie en daarna, in de moderne modus, een lagere renderschaal; wordt het rustiger, dan probeert het weer een trap hoger.

  6. Presentatie

    1986: 320 × 200 naar een 4:3-kader met nearest-neighbour en 4 bits per kanaal. Modern: FXAA als lichte tweede pass op volle resolutie.

Geen dependencies, geen buildstap

JavaScript-modules, drie GLSL-shaders en Web Audio, meer niet. De demo draait onder dezelfde strikte Content-Security-Policy als de rest van mahieu.nl: geen externe scripts, geen inline code.

Pauzeren zonder tijdsprong

Een eigen animatieklok bewaart de verstreken scènetijd. Pauze en hervatten verspringen niet, en de snelheid van 0,01× tot 2× geldt voor beeld én geluid, ook tijdens het spelen.

Een verloren WebGL-context herstelt zich

Verliest de browser de grafische context, bijvoorbeeld bij een tabwissel op een telefoon, dan bouwt de demo de renderer opnieuw op en hervat hij of keert hij netjes terug naar het startscherm. Ook vanuit een pauze.

Getest in een echte browser

82 unittests voor scène, camera, klok, audio, framebudget en de contracten van bediening en renderer, plus 20 Playwright-tests in Chromium voor bediening, focus, pauze, audio, fullscreen, contextverlies en de gouden beelden van de 24 poses. Toetsenbordbediening, focusvolgorde en de status voor schermlezers horen bij de tests.

Bronnen en credits

Gebouwd op wat bewaard is gebleven

Deze hommage bestaat dankzij mensen die het origineel hebben bewaard, teruggevonden en uitgeplozen. Dit zijn de bronnen waarop de reconstructie steunt.

Eric Graham — Raytracer 1.0 (1987)

De teruggevonden broncode (rt1.c–rt3.c) en het scènebestand robot.dat, door AlphaPixel van de originele diskette gehaald en gepubliceerd op GitHub. Eric Graham gaf in februari 2026 toestemming: iedereen mag de code gebruiken, zolang hij genoemd wordt. Bron van materialen, vloer en shadingmodel.

Ernie Wright — technische analyse

De 24 originele frames als PNG, de opname juggler.wav van een echte Amiga en de geschiedenis van de demo, inclusief Grahams eigen herinneringen. Het kalibratie- en verificatiemateriaal van deze reconstructie.

Meatfighter — wiskundige reconstructie

Een stapsgewijze herbouw met de afleiding van camera, balbanen en het shower-jongleerpatroon: een hoge boog van links naar rechts en een lage boog terug. Gebruikt als vergelijking voor de hoge en lage baan.

Ben Hanke — realtime WebGL-versie

Een eerdere realtime versie van de Juggler, gebruikt als vergelijking voor een camera-onafhankelijke 3D-pose van de figuur.

AmigaWorld, mei/juni 1987

Grahams eigen artikel over het maken van de Juggler. Het programma dat het artikel aankondigde werd Sculpt 3D. De demo zelf verscheen als versie 1.2 op Fish Disk 97.

Waarom dit telt

Meten in plaats van gokken, ook bij een jongleur uit 1986

Wat het laat zien. Dat een raytracer geen renderfarm meer nodig heeft: wat in 1986 een uur per beeld kostte, doet een grafische kaart nu tientallen keren per seconde, in een browser, zonder plug-in. En dat je uit 24 plaatjes en een stuk C-code een werkende, geteste en toegankelijke toepassing kunt bouwen, als je bereid bent te meten in plaats van te gokken.

Aansluiting op het aanbod. Dit is dezelfde manier van werken als bij opdrachten: beginnen bij wat er is, de kleinste werkende versie bouwen, meten, testen en pas dan uitbreiden. Dat is wat van idee naar werkend bewijs betekent.

Controleerbaar bewijs

Wat de Amiga Juggler aantoonbaar doet

Probleem
De originele Juggler bestaat alleen nog als 24 HAM-frames; camera- en bewegingsdata zijn verloren en de demo is gebonden aan het Amiga-scherm van 1986.
Aanpak
Scène, camera, licht, beweging en geluid teruggemeten uit de frames, de teruggevonden broncode van 1987 en een audio-opname; opnieuw gebouwd als analytische raytracer in een WebGL2-fragmentshader, met een 1986-modus en een vrije camera.
Status
Live en publiek op www.mahieu.nl/amiga-juggler, in het Nederlands en Engels. Een persoonlijk maakproject, niet in opdracht gemaakt.
Aantoonbaar resultaat
70 bollen, harde schaduwen en twee spiegelreflecties per frame live berekend; de 24 originele poses en het ‘ding’ gereproduceerd; 82 unittests en 20 browsertests slagen.
Beperkingen
WebGL2 is vereist en zonder Web Audio speelt de demo zonder geluid; op zwakke grafische kaarten schakelt de demo terug naar één reflectie en een lagere renderresolutie. HAM-kleurfringing wordt niet geëmuleerd. Geluid vereist een klik of tik.
Stand van zaken

Veelgestelde vragen

Goed om te weten over de Amiga Juggler

Wat is de Amiga Juggler?

Een animatie van Eric Graham uit 1986: een jongleur met drie spiegelende ballen, geraytracet op een Amiga 1000 en door Commodore uitgebracht als demonstratie. 24 frames in HAM-modus, 320 bij 200 pixels, een lus van 0,8 seconde met één ‘ding’.

Is dit het originele filmpje?

Nee. Er wordt geen enkel origineel beeld afgespeeld: elke frame wordt live berekend in uw browser, op uw grafische kaart. Scène, camera en beweging zijn teruggemeten uit de 24 frames en de broncode van 1987.

Waarom ziet de 1986-modus er zo blokkerig uit?

Omdat dat de echte resolutie is: 320 bij 200 pixels, uitgerekt tot 4:3 zoals op een monitor van toen, met 4 bits per kleurkanaal en 24 stappen per lus. Kies Modern voor de vloeiende weergave op volle resolutie.

Werkt het op een telefoon?

Ja, in een moderne browser met WebGL2. Slepen draait de camera, knijpen zoomt en twee vingers verschuiven het beeld. Blijven frames traag, dan schakelt de demo zelf een trap terug: eerst naar één spiegelreflectie, daarna naar een lagere renderresolutie.

Mag dit zomaar?

Het is een hommage, geen kopie: er worden geen originele beelden gebruikt. Eric Graham heeft in 2026 laten weten dat iedereen zijn code mag gebruiken zolang hij genoemd wordt; de demo en deze pagina noemen hem en de andere bronnen.

Wat heeft dit met uw werk te maken?

Alles: beginnen bij wat er is, meten in plaats van gokken, de kleinste werkende versie bouwen en die testen. Zo werk ik ook bij opdrachten, van idee naar werkend bewijs.

Contact

Vragen over dit project of de gebruikte techniek?

Stuur gerust een bericht.

Liever direct? Bel +31 6 1400 2312 of mail mwd@mahieu.nl.