Thirty Years of POV-Ray: From the Family PC to the Browser

In July of 1996 I was fifteen years old, and the family PC was mine whenever nobody else was looking. That summer I found POV-Ray. It came with a folder of example scenes - checkered floors, glass spheres, chess sets - and a scene description language I only half understood. That didn't matter. I changed numbers until things looked cooler. I pointed cameras at the wrong things on purpose. I turned still scenes into animations by rendering frame after frame overnight, the whole machine devoted to one image at a time, and stitched them together in the morning. My knowledge was teenager-sized: small. My determination was also teenager-sized: enormous.

This month, thirty years later almost to the week, I put that same raytracer - the actual POV-Ray, not a tribute act - inside a browser tab - a concept that did not even exist in 1996, when Netscape gave you exactly one window and you liked it. It renders the same 1993 example scenes I tinkered with at fifteen. The renders that took my family's PC all night take about two seconds. And this time it has an AI copilot that fixes my scene files when I break them, which fifteen-year-old me would have found indistinguishable from magic.

You can try it right now: POV-Ray Studio. This post is the story of how it got there.

The classic glass sphere over a checkerboard, ray-traced by POV-Ray running in WebAssembly

The itch, and an honest fork in the road

It started with an image search: that grid of glass spheres on checkerboards that every raytracer demo has produced since the eighties. The question was simple - what would it take to port POV-Ray to JavaScript, editor and animation scheduler included?

There were two honest paths. Path A: compile the real thing - half a million lines of AGPL C++ - to WebAssembly with Emscripten, unknown weeks of build archaeology. Path B: write a toy raytracer that speaks a subset of POV-Ray's scene language, a weekend of work, but forever a tribute act. The classic scenes would never quite render right, and the whole point was the real thing.

Path A sounded like weeks of build archaeology. It went considerably better than that.

The 54-minute spike

The recon gods were kind. POV-Ray 3.8 had already modernized away its hairiest dependency - threading moved from boost to std::thread, and every remaining boost usage turned out to be header-only. Emscripten ships boost headers, zlib, libpng, and libjpeg as built-in ports. The whole build came down to a hand-written config.h, one three-line patch to the platform detection header, and a compiler flag POV-Ray requires of unofficial builds - you must literally sign your work:

#elif defined(__EMSCRIPTEN__)
    // Emscripten/WebAssembly detected. Use plain POSIX settings (musl libc).
    #include "syspovconfig_posix.h"

The result: a 2.3 MB wasm binary. The glass sphere renders at 640x480 with antialiasing in 1.7 seconds across 8 threads. And the kicker - CHESS.POV from the official 1993 POV-Ray 2.0 sample archive, a scene file older than some of my coworkers, parses and renders unmodified:

The famous 1993 CHESS.POV sample scene rendered by POV-Ray 3.8 compiled to WebAssembly

Getting it into a browser tab (the war stories)

Node was easy. Browsers charge admission:

  • Never run a 1990s main() on the UI thread. The first attempt froze the tab so hard it took the testing browser down with it. POV-Ray blocks in main() while its worker threads render; in a browser that means the page dies. Emscripten's PROXY_TO_PTHREAD moves main() into a worker and the page stays alive.
  • Threads need permission. Multi-threaded wasm requires SharedArrayBuffer, which requires cross-origin isolation headers, which - because they break third-party embeds - you only want on one route. This Flask site sets them per-path, with one subtle trap: Flask after_request handlers run in reverse registration order, so the header override has to register before the security middleware or it edits headers that don't exist yet.
  • The wasm itself needs a CSP exemption ('wasm-unsafe-eval'), also scoped to the one route.
First successful in-browser render: crossOriginIsolated true, 8 threads, glass sphere in 2.95 seconds

Building the studio around it

An engine is not a tool. Around the wasm went a CodeMirror editor with a hand-rolled syntax mode, the full 1 MB standard include library loaded into the browser's in-memory filesystem, and 57 example scenes - most straight from that 1993 archive, each one batch-tested before inclusion. The testing surfaced bugs my Mac had been politely hiding: macOS filesystems are case-insensitive but the browser's is not, so scenes referencing Colors.inc worked locally and died in production; scenes pull sibling assets through image_map statements no include-scanner sees; and POV-Ray's option parser rejects paths containing spaces, which an iCloud-synced repo has in abundance. The fix for all of it was the same lesson from every era of computing: test in an environment identical to where you ship.

Animation, and the export saga

POV-Ray animates by rendering a frame per tick of a clock variable, exactly like I did in 1996 - the studio just loops it for you with a scrubber and a progress bar. Getting those frames out as video turned into its own saga. The browser's MediaRecorder produces files with no duration header (upload validators reject them), can't be previewed on Safari when they're WebM, and on Chrome will happily hand you VP9 in an MP4 costume, which Bluesky's video pipeline spits out. The endgame: the studio holds every rendered frame anyway, so it encodes video itself - WebCodecs H.264 with fixed timestamps muxed into a proper seekable MP4. Uploads work everywhere now.

Long animations then hit a memory wall - keeping decoded frames in RAM costs about 3 MB each - so frames now stream to origin-private disk storage (OPFS) and decode on demand. And when someone tried an 8000x8000 render, the only thing stopping it turned out to be my own conservative size clamp: with the wasm memory ceiling raised to 4 GB, a 64-megapixel glass sphere renders in two minutes nineteen seconds. In a browser.

The Cube Mark: the nerdymark logo on a spinning cube over a checkerboard under a starfield, in the site palette

Sparkle: the AI copilot

Here is where 2026 stops pretending to be 1996. The studio has an assistant - bring your own Gemini API key, stored only in your browser, sent only to Google, with the whole integration readable and a security policy that physically cannot leak it anywhere else. It explains scenes, prettifies lighting, adds loop-safe animation, and generates scenes from a sentence.

The part I am proudest of: when a render fails, the studio already has the parser's error output, line numbers and all. One click sends the broken scene plus those errors to the model, applies the corrected file, re-renders, and repeats up to three times. Watching it fix a deliberately mangled scene on the first attempt - closed the vector, corrected a bogus keyword, re-rendered clean - was the moment this stopped feeling like a port and started feeling like a new kind of tool. Building it even exposed a real engine bug: parse errors abort the wasm runtime in a way that used to freeze the studio permanently. The AI feature forced the fix.

Costs are measured, not vibes: we benchmarked real asks and published the data. A typical ask on the default model runs about nine-tenths of a cent. The studio counts your exact tokens as you go.

Sharing, thirty years apart

In 1996, showing someone your render meant the school computer lab and a floppy disk. Now the studio posts directly to Bluesky - your own app password, kept in your browser, talking straight to your own server. Stills embed as images; animations upload through Bluesky's video service as real video posts. Getting links to render as clean text instead of truncated URLs meant learning atproto richtext facets, and the one-embed-per-post rule means a media post can never carry a link card - the kind of API trivia you absorb at 1 AM and write down so the next person doesn't have to.

And yes, there's a 1995 mode

Because POV-Ray shipped in 1991 and the sample scenes are from 1993, the studio has a toggle that dresses it the way the web looked when I first met it: Win95 teal, beveled buttons, Times New Roman, a blinking NEW badge, and a visitor counter that starts at 31337. One click back returns you to 2026. The changelog is on the page - eleven releases and counting.

Thirty years

Most of this was built across a handful of evenings, working conversationally with an AI assistant. The part that stays with me, though, is simpler. Fifteen-year-old me changed numbers in scene files he barely understood and waited all night to see what happened, because watching light bounce through a glass sphere felt like being let in on a secret. Forty-five-year-old me can hand that exact feeling to anyone with a browser tab: same raytracer, same scenes, two-second renders, no floppy disks required.

Teenager-sized determination turns out to compound nicely over thirty years.

Open POV-Ray Studio and go change some numbers until something looks cooler.

More in Technology