The Juggler

Apparently your browser doesn't support the HTML5 canvas element. Sorry about that.

Rendered in Your Browser

The Juggler image on this page is not a JPEG or a PNG. It is drawn dynamically by a Javascript program that runs in your browser when you load this page. The program is a Javascript translation of the raytracer that produced the original Juggler animation in November of 1986. Eric Graham described his raytracing software in AmigaWorld the following spring, and he distributed the C source code on his Raytracing 1.0 disk, which he sent out by mail order for $15.

Each frame of the original Juggler animation took about an hour to render on an Amiga 1000. The above image has five times as many pixels as the original frames (640 × 480 versus 320 × 200) and would've taken about five hours to render.

Jimmy Maher, author of the MIT Press Platform Studies book about the Amiga, The Future Was Here, contacted me in 2010 while researching his book. (Highly recommended, by the way.) Among other things, he was looking for the Raytracing 1.0 disk, but I'd lost my copy years earlier. We both asked around. Jimmy contacted Eric himself, and at one point we were mailing Amiga floppies back and forth across the Atlantic (Jimmy was in Denmark at the time). But we came up empty.

Then in January, 2014, Patrick de Zoete of the Netherlands let me know that he'd found a copy of the disk in an online auction. He made it available online in the form of an ADF file, the floppy disk image format used by Amiga emulator software. Once I'd reinstalled my copy of Cloanto's Amiga Forever emulator, I could see that this was indeed what we'd been looking for.

The RT 1 disk includes three C source code files for a raytracer program (rt1.c, rt2.c, rt3.c) as well as robot.dat, a sort of scene file comprising the spheres, camera, light, and ground plane colors of the Juggler universe. I've translated both the C code and the scene file into Javascript.

Differences

A frame from the original Juggler (top) compared with the image rendered on this page.

This image differs in a number of ways from the original Juggler animation. Eric Graham created the scene description for this image specifically for his AmigaWorld article (May/June 1987, p. 19). The camera and light have been moved and the pose of the robot altered. The robot's arms have been shortened, giving them more human proportions while possibly making it more difficult for the robot to actually juggle. And it's not clear that the spheres are in physically plausible positions.

Although I tried to preserve the spirit of the original code, my Javascript translation takes a few liberties. Javascript and C have different ideas about scope and data structures, and the execution environment of a Web browser differs in important ways from the Amiga's operating system. In one case, I replaced a 25-line, somewhat dodgy vector reflection calculation with what is now the standard algorithm, expressed in 4 lines. I also chose not to render the image in 12-bit hold-and-modify (HAM) colors. I think the straight 24-bit color rendition gives a better sense of what this looked like through the eyes of 1980s computer users.

Performance and Compatibility

This Web page requires support for HTML5. I've tested it in Firefox, Safari, Chrome, Internet Explorer, and Opera running in Windows, Mac OS, iOS, and Linux. Up-to-date versions of these browsers can all render the complete image. Browsers that are a couple of years old (as of early 2014) may support the <canvas> element but not the requestAnimationFrame() method. The symptom in this case is that only the top 16 lines of the image appear on the page. Older browsers that don't support <canvas> at all will report this in the space where the image would have rendered.

In my tests, the image rendered in a few seconds on desktops and laptops, and in about 25 seconds on my iPad 3. Firefox 24 ESR running on my CentOS 6 workstation has a horrendous performance bug in its Javascript interpreter; it takes a couple of minutes to render the image, during which the entire browser feels sluggish and unresponsive. But it does eventually render the image.

Updates

Shadows using the original (left) and revised values of raytrace epsilon.

May 4, 2014 | Edd Biddulph noticed that the shadows under the Juggler's eyes looked wrong, as if the eyes were floating above the face. He correctly surmised that this was a symptom of the raytrace epsilon value, called SMALL in the code, being too large. He suggested a modest change in this value from 1.0e-3 (0.001) to 1.0e-5 (0.00001).

Raytrace epsilon, sometimes called ray bias or offset, is a small fudge factor common to a lot of raytracers. When a shadow ray is fired from the surface of an object, tiny roundoff errors in the ray calculation can cause the ray to hit its own origin, rather than traveling out into the scene. The resulting shadow artifacts manifest as random dark splotches known as raytrace acne. The underlying roundoff error is an unavoidable consequence of the finite number of bits available to represent floating-point numbers.

The raytracer ignores hits that are closer than epsilon to the ray origin. In effect, this offsets the origin of the ray above the surface by a distance equal to epsilon. Epsilon is typically a user parameter, since it depends on the scale of the scene. If it's set too small, you get acne, or false positives for the shadow test. If it's set too large, you get false negatives, places on the object surface that should be in shadow but aren't. Visually, the rendered shadow appears to be displaced from its source.

On the Amiga, a combination of low resolution, 12-bit color, and HAM display mode noise acted to disguise the effect of SMALL not being small enough. Now that I'm rendering without those limitations, it's appropriate to adjust the value of SMALL, and I've made the change. Thanks to Edd for pointing out this interesting problem.

I've also restored Eric's original gingham() code. This is the function that figures out which of the two tile colors should be used for a ray hit on the floor. At first glance, Eric's floor is a simple checkerboard, which should only require a modulo-2 operation to generate. I had replaced his somewhat verbose code with a one-liner that created a true checkerboard.

But Eric's floor isn't a true checkerboard. The pattern is reflected about the axes, producing a 6 × 6 yellow tile at the origin and 3 × 6 or 6 × 3 rectangular tiles on the axes. The floor of the rendering now matches the original in this regard.

Finally, I added a paragraph pointing out the differences between the image rendered here and the original Juggler animation frames.