Maybe Human

How I built it · July 31, 2026

Rendering gems that don't exist yet

The engineering and the product thinking behind a tool I built to see a stone before it was cut.

This is the companion to The ring I didn’t make. That piece is about why I built it. This one is about how, both the engineering and the product thinking, because a friend asked and because I think the shape of it is pretty.

The core problem is that you cannot photograph a thing that does not exist. I wanted to see a specific faceting design, in a specific material, in a specific shade, and that exact combination had never been cut, so there was no photograph of it anywhere. The whole tool exists to answer one question: what would light do inside this stone, if the stone were real.

What a design actually is

A faceting design is not a picture, it is a set of instructions: a list of angles and index positions that tell a cutter exactly where to grind each facet, plus the proportions of the finished stone. The ring essay shows one of Akhavan’s diagrams; that diagram is just a drawing of the angle-and-index file my renderer reads to build the stone.

Faking the light honestly

You answer that question by simulating the light instead of the stone. I used Blender, a 3D program, and its path tracer, called Cycles, which works by following rays of light as they bounce around a scene and add up to a color at each pixel. A gem is nothing but a machine for doing interesting things to light.

The heart of it is a single reusable material I called the adjustable gemstone. It has three knobs. The first is the index of refraction, which is how sharply the material bends light as it enters, the thing that makes a straw look broken in a glass of water. The second is dispersion, which is how much the material fans white light out into separate colors, and that fanning is what jewelers call fire. The third is the body color. Every real gem material is just a particular setting of those three numbers. Garnet bends and disperses one way, sapphire another, moissanite much more strongly, diamond somewhere in between. So to render the same cut as a different stone, you do not rebuild anything. You change three numbers and press go, and the fire changes with them.

So here is one of his cuts, “Sierpinski’s Nest,” rendered three ways by changing only those numbers. Same geometry, same lighting, three very different stones. Blue sapphire barely fans the light. Sphene throws more fire than diamond. Sphalerite disperses light more than three times as hard as diamond does, and the stone comes apart into full spectrum.

Akhavan's Sierpinski's Nest cut rendered as blue sapphire, deep blue with restrained fire The same Sierpinski's Nest cut rendered as sphene, a golden-green stone throwing colored fire as it turns The same Sierpinski's Nest cut rendered as sphalerite, an amber stone breaking light into a full spectrum
Akhavan's "Sierpinski's Nest," the same cut rendered as blue sapphire, sphene, and sphalerite. Only the index of refraction, the dispersion, and the color changed. Watch the fire go from restrained in the sapphire to unhinged in the sphalerite.

They have to be animations, not stills, because fire only exists in motion. A still frame catches one accidental instant of it. The stone has to turn for you to see the light actually move across the facets, which is the entire thing you are trying to judge.

A catalog is a cross product

One render is a design, plus a material, plus a camera move. Once you see it that way, the whole catalog falls out on its own: it is every design crossed with every material crossed with every camera setup. And I did run the whole thing. Something like seven hundred designs, against more than two dozen real gem materials, in three camera moves each, which is on the order of fifty thousand animations and about ninety-five gigabytes. There was no way I was tracking that by hand.

So I put it in a database, in plain tables. A design is a faceting pattern, with its author and its shape and its license. A material is a row holding those three physics numbers, the refractive index, the dispersion, and the color. An animation is a camera and lighting move. A rendering is the row that says “this design, in this material, filmed this way, produced this video file.” And a file row points at the finished video, stored by the fingerprint of its own contents so that an identical render is never stored twice. The video files themselves live in cloud object storage, not in the database.

Entity-relationship diagram of the render catalog: DESIGN, MATERIAL, ANIMATION, and FILE tables each connect to a central RENDERING table. A rendering references one design, one material, one animation, and one file; the file stores the video by its sha256 fingerprint.
The whole catalog as five tables. Every rendering points at one design, one material, one camera move, and one file.

The queue is a query

To render fifty thousand combinations, you need a to-do list of every one that has not been made yet. The obvious move is to stand up a queue service and enqueue jobs. I did not need one. The to-do list is just a question you can ask the database directly: cross every design with every material with every camera move, subtract the combinations that already have a finished rendering, and take what is left. In SQL that subtraction is a single left join with a “where the match is missing” filter. The next job to render is the top row of that result. The count of work remaining is the number of rows.

-- The entire to-do list: every design x material x camera move
-- that does not yet have a finished rendering.
SELECT d.id AS design, m.id AS material, a.id AS animation
FROM design d
CROSS JOIN material  m
CROSS JOIN animation a
LEFT JOIN rendering r
       ON r.design_id    = d.id
      AND r.material_id   = m.id
      AND r.animation_id  = a.id
WHERE r.id IS NULL          -- the "where the match is missing" filter
ORDER BY d.id, m.id, a.id
LIMIT 1;                    -- the next job. Drop the LIMIT and it counts the backlog.

That gave me something a hand-built queue does not: it is idempotent and resumable for free. If a render fails, or my laptop sleeps, or I add a new material next week, I do not reconcile anything. I just ask the same question again and it tells me exactly what is still outstanding, including every combination of the new material with every existing design. It cannot fall out of sync, because it is computed from the same rows the workers write.

I built this over evenings, pair-programming with ChatGPT, whose first instruction in my notes is literally “you are a coding colleague.” There was a second, cruder path through a real cloud queue that I wired up first, and one of my actual commit messages from that week reads “hacking workaround for not using a proper queue, we will fix later.” The first pass had rough edges, wrong argument names, a couple of values passed as the wrong type, the usual. The clean version I kept was not new: years ago at a job I ran the same producer/consumer setup, workers pulling the next unclaimed job straight from a database, so once the cloud-queue version annoyed me enough I just reached for a pattern I already knew. You do not always design the elegant thing first. Sometimes you build the clumsy one, feel where it rubs, and remember you already had the fix.

What it took to actually run it

Fifty thousand path-traced animations is far more than one machine will ever render alone. Each frame is its own light simulation, and a single turning stone is hundreds of frames. So the harder half was never the renderer. It was running many renderers at once without them colliding.

They never had to coordinate, because the database already did it for them. Every worker runs the same small loop: ask the catalog for the next combination that has no rendering yet, render it, upload the video, write the row that marks it done. Two workers can ask at the same time, and nothing coordinates them. They fan out across the list, and on the rare chance two grab the same job it does not matter, because the finished video is stored by the fingerprint of its contents, so the second render just lands on top of the first instead of doubling it. To go faster you add machines. Ten workers drain the list about ten times as quickly, and not one line of the code changes, because the only thing they share is a question they all ask of the same database.

The Postgres catalog runs an anti-join query for every design, material, and camera move that has no rendering yet. A horizontally scaled fleet of Blender render workers each pulls the next job, path-traces it, writes the video to an object store, and inserts the rendering row, which shrinks the query.
The catalog renders itself. The work list is a query, the workers are interchangeable, and you scale by adding more of them.

I described the whole arrangement in code rather than clicking it together, so one command stands the entire thing up and one command takes it all back down. That fit the shape of the work, which spikes to a fleet for a weekend of rendering and then should cost almost nothing while it sits idle. Bring the workers up, drain the list, take them down.

The whole stack provisioned from one infrastructure-as-code file: the file provisions a private network holding the managed Postgres catalog, the Blender render workers, and the serverless search function, with object storage for the videos and a content delivery network alongside.
Every part named, provisioned from a single file. One command brings it up, one command takes it down.

The one interesting idea is that the database is the queue; everything else is ordinary infrastructure.

Getting it in front of a person

The renders are useless in a folder, so there is a small web app over the top, a searchable catalog. You filter by the cutter, or the shape, or the name, and you get back each design with its diagram and every material it has been rendered in, ready to play. Behind that one search box are two very different paths, split on purpose.

Serving architecture: the search UI sends small text lookups to a serverless API that reads the Postgres catalog, while video playback goes through a content delivery network that caches the MP4s at the edge and falls back to the object store, which is reachable only through the CDN.
Small catalog lookups take the cheap serverless route; heavy video takes the edge-cached route. The store never faces the public directly.
Akhavan's Glass Shard Vortex faceting design rendered as blue sapphire, turning to show the fire move across the facets
One catalog entry: Akhavan's "Glass Shard Vortex," rendered as blue sapphire.

The videos are served through a content delivery network, which is a fancy way of saying copies sit on servers near the viewer so they start fast, because a slow catalog does not get used. And the files are compressed carefully, since fire needs motion and motion costs bytes, and the whole thing dies if each stone is a five-megabyte download.

The product question I did not answer

The engineering was the easy half. The harder question is who this is actually for: someone commissioning a custom stone, who has to choose a cut and a material and a shade they cannot see anywhere, and who is about to spend real money on a rock based on imagination. The tool lets them browse the exact combinations, in motion, before they commit. It is a real need, just a narrow one.

Two product instincts I do stand behind. First, let people search by what they actually care about, which is the cutter and the shape and the feel, not by the physics numbers, even though the physics numbers are what the machine runs on. Second, credit the makers. These faceting designs are somebody’s art. The one I keep coming back to is Arya Akhavan, a physician who cut most of his best designs in the margins of medical training and gives them away for free, and it would be wrong to render his work as if the shapes fell from the sky. So the author and the license are first-class fields on every design, carried all the way to the page, because standing on someone’s craft means naming them. Every design here comes from the free archive at The Gemology Project.

I never needed the tool for my own ring. We bought a diamond in a shop and it was exactly right. So it stayed a demonstration rather than a business. But the bones of it, simulate the thing instead of guessing at it, model the catalog as a cross product, let the database be the queue, store files by their fingerprint, are patterns I picked up here and used again, later, on much bigger things. This is where I learned them, on something I loved.

Companion Read why I built it
Keith Gargano · maybehuman.io keith@maybehuman.io