cd /news/ai-tools/building-ribbonlab-a-3d-animated-bac… · home › topics › ai-tools › article
[ARTICLE · art-169206] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

Building Ribbonlab, a 3D Animated Background Generator

A developer built Ribbonlab, a free browser-based editor for 3D animated backgrounds, over a few evenings using Claude Code to write most of the code while directing fixes by describing visual bugs. The work focused on three areas: procedural textures (silk, moiré, guilloché, chameleon paint, mercury, fibre optic), the ribbon's shape, and the settings panel, with per-triangle lighting replaced by corner-computed lighting blended across each triangle so switching textures preserves the ribbon's base color.

by read11 min views3 publishedNov 13, 2026

Originally published on ayurchenko.xyz. The image above opens the live editor. Ribbonlab is a free browser editor for 3D animated backgrounds. I built it on my own over a few evenings of spare time. Claude Code wrote most of the code. I decided what the tool does, looked at every change in the browser and said what was wrong until it looked right. This piece shows that work on the three parts that took the most of it: the textures, the shape and the settings.

Every change went the same way. I opened the editor, looked at the ribbon and wrote what was wrong in one line, usually with a screenshot: one edge of the wave looks buggy, the glitter sits on top and does not move, this texture darkens the ribbon. Claude Code found the cause in the shader or the panel and fixed it. I checked the result in the browser and either moved on or sent the next line. The repository has 243 commits from 8 to 22 October, most of them one change I could see.

The editor on the Aurora preset. The ribbon fills the page and the panel stays to one side.

One version of the silk read like a muscle with veins. The threads were thick and contrasty, and the light lay across the surface in wavy bundles, pale on dark blue. The fix was a smooth surface with fine, straight threads along the band. Then I tuned the threads by eye and kept the values: Streaks from 0.28 to 0.44, Glow from 1.3 to 0.89, Grain from 3.5 to 3.1. At those values the silk reads as millions of threads.

Left: the silk that read like a muscle. Right: the silk that shipped.

Next I asked for textures as good as the silk, and the answer started with why the silk worked. Its threads follow the band, so every twist bends the pattern with it. Their light depends on how the surface turns, so the pattern moves when the ribbon moves. Leopard, zebra and polygonal were prints laid on top, and they stayed flat. The textures added after that follow both rules: moiré from two layers of threads drifting in and out of step, guilloché lines that swell in shadow and thin in light, chameleon paint whose hue turns with the viewing angle, mercury that mirrors a studio with softboxes sliding over it, and a twill weave. Snake did not look good, so it went.

Glitter took two rounds. The first one lay on the ribbon like a still sticker. Now each flake is fixed to the fabric, twinkles on its own beat and flares when its patch turns to the light. Then the flakes lined up in rows, so they are scattered over two layers, turned and shifted against each other.

Fibre optic took three. The first version was even dark stripes. It became two layers of strands with uneven spacing, each strand with its own thickness and brightness, light gathering in patches that drift down them, and a few strands in the palette’s accent colors. Then it made the whole ribbon darker. It switched off the silk threads that give the ribbon its pale edge, dimmed the base by 10%, and far strands blurred into a grey veil. Now the strands glow over the silk threads, and the ribbon is as bright as on Silk.

Fibre optic on the same violet ribbon. Left: the first version. Right: the one that shipped.

Lenticular showed that some sliders did nothing. So every Surface slider was tried on each of the 23 textures. Streaks worked only on silk, and now they show only for silk and fibre optic. Texture strength did nothing above 1 on half the textures. Now 0 turns any texture off, 1 is its designed look and 2 doubles it. Pattern density was added for every texture that has a pattern.

Two weeks later I saw polygons on Satin, one per triangle of the mesh, and noticed that picking a texture changed the ribbon’s base color. The light on the band was computed once per triangle. It is now computed at the corners and blended across. Every texture now keeps the average light of the silk threads, so switching textures keeps the color. In the same pass, Mercury and Liquid chrome reflected in grey and lost the ribbon’s color, and Pearl and Lenticular bleached it toward grey. All four are now lit in the palette’s colors. Chameleon and Holo film stayed as they were, because shifting the color is the point of them.

Mercury on the pink Sakura palette. Left: a grey mirror. Right: the same mirror lit in pink.

Fibre optic, glitter, brushed metal and pearl, each from one of the presets.

The first ribbon was a folded band whose two layers twisted apart. I asked for one band with three plain controls: width, length and thickness. Folded stayed as a second form.

Then one edge of the band looked broken while the other was clean. The edge is seen side-on, and its light jumped from one row of the mesh to the next, drawing ticks across it. The edge got its own smooth light. Near one side of the band the faces sheared apart under the width twist, so the twist now holds rigid over the outer 8% of the width.

The Shape folder holds the form, width, length and thickness, three twists (along the length, across the width and in depth), a falloff for each twist that sets how quickly it fades, and Wave, the size of the slow waves rolling through the band. I moved them by eye until the ribbon swept across the frame the way I wanted, copied the values out as JSON and made them the defaults. The changes look small in numbers:

| Control | Start | Default now |

|---|---|---|
| Twist length | -0.65 | -0.36 | 

| Twist width | 0.41 | 0.62 | | Twist depth | -0.58 | -0.19 | | Width falloff | 0.70 | 0.88 | | Length | 400 | 432 | | Thickness | 8 | 2.5 |

Same palette, same seed. Left: the starting values. Right: mine, now the default.

Turning the ribbon got its own controls in View: Rotate X, Y and Z sliders, and an axes widget in the corner. Clicking the end of an axis turns that axis toward you, the short way round. Randomize keeps the shape near the default composition. The twists, falloffs and wave move within narrow ranges (Twist width 0.25 to 1, Wave -12 to -3) and the seed does the rest. With wide ranges the ribbon slid to the edge or filled the whole frame.

The Shape folder on the default ribbon.

The panel has seven folders in the order people tune a background: Color, Adjustments, Shape, Surface, Effects, Motion and View. On the first visit only Color is open. Brightness first sat in Effects. It changes the color, so it moved to a new Adjustments folder with saturation, contrast and hue. Each effect is a switch, and its sliders sit in the same card only while it is on.

There are 53 palettes in eight groups by hue, each with two to six colors, and you can add or remove colors within that range. Picking a palette changes only the colors. Every other slider stays where you put it, so trying palettes never throws work away.

The white palettes exposed a setting nobody could see. A grey ribbon came out blue. One line in the color shader cooled every shaded side: red dropped to 45% and blue only to 75%. It gave the blue and violet ribbons their depth and made grey and warm ones look dirty. I made it a slider, Shadow warmth, with the usual cool shadows in the middle, deeper blue to the left and warm to the right. Each palette sets it when picked. Greys and warm palettes go neutral.

Cobweb, a white palette. Left: the old fixed cool shadows. Right: the neutral shadows it now sets itself.

Neutral values also sat in odd places on their sliders. Brightness ran from 0.3 to 2, so its neutral 1.0 sat at 40% of the track. Ranges with a neutral point are now symmetric, and the neutral value sits in the middle. The slider called Warmth turned out to mix in the accent colors, so it is called Accent mix now.

Randomize works on one folder with the dice on its header, or on everything at once with G. Each slider draws from a range where results look good instead of anywhere on its track: brightness 0.92 to 1.12, saturation 0.85 to 1.3. Loud settings move only sometimes: hue and rainbow in 15% of rolls, iridescence in 30%. Randomize all keeps your texture, because a texture suits some looks and not others. The Surface dice picks textures by weight, Silk most often, and skips Liquid chrome, Marble and Sequins, which gave muddy results at random. Randomize and Reset both show an Undo.

Presets went through ten placements, each prototyped in the working editor: a window of rendered ribbons, a folder in the panel, a stage with the preset name, a card deck and more. Each one covered the ribbon or competed with the panel. What shipped takes no space. The left and right arrow keys flip ten presets with a crossfade, and a small pill names each one, then hides.

Two of the ten preset prototypes: a window of ribbons and a stage.

A background video has to repeat without a jump. With Seamless loop on, the wave blends into the same moment one loop earlier, so the end meets the start. Glitter, sequins and fibre optic run on their own timers, and those blend the same way. Auto-turn makes whole turns per loop. The first export recorded the canvas in real time, added a stray frame at the start and lost frames at the end. Export now renders the loop frame by frame with exact timestamps, so a two-second loop is exactly 60 frames. I asked why export offered two or three loops of something that repeats anyway, so a loop exports as one loop, with its length taken from the Loop length slider in Motion. GIF is offered only for loops.

Export also gives code, and the code went through four designs. A standalone HTML file handed out the whole editor. An npm package would have published the renderer for anyone to copy. A short id with a script tag worked until Framer turned out to strip script tags from embeds. An iframe of the ribbon’s own page shipped: it loads in Webflow, Framer, WordPress and Shopify, React gets a script snippet and Notion a plain link. Each player version is frozen once it ships, so a ribbon on someone’s site never changes when I change the look. I also built a lock that kept each ribbon to named sites, and removed it, because it made the first use harder for everyone. Export, Code tab. One snippet, with a note on where it goes in each site builder.

PageSpeed Insights first reported Error on every metric. Its test browser has no GPU, and opening WebGL in software froze the page for one to two seconds. The page now checks the GPU in a background worker, shows a still of the ribbon where there is no GPU, and starts the renderer after the page has painted.

PageSpeed Before After
Mobile 31 80
Desktop 41 98

Claude Code wrote most of the lines. The decisions in this piece are mine: what each texture should look like and when it was good enough, which sliders exist and in what order, which ranges random values may take, and what to cut. Each one was judged in the running product, so ten versions of one decision took a day.

I work the same way with teams, from deciding what to build to a working release. See Fractional Product Design, start with a Product Diagnosis, or write to me.

Yes. The editor, every export and the embed code are free, and there is no account to create.

Open Ribbonlab, tune a ribbon, then choose Export and Code. The HTML snippet goes into a code embed in Webflow, Framer, WordPress or Shopify, React projects use the React snippet, and the guides show each builder step by step.

Twenty-three, in three groups. Fabric: silk, satin, velvet, twill, matte and latex. Metal and shine: mercury, liquid chrome, brushed metal, chameleon, holo film, lenticular, fibre optic, pearl, glitter and sequins. Pattern: moiré, guilloché, marble, topographic, leopard, zebra and polygonal.

Yes. Turn on Seamless loop in the Motion folder, set the loop length, and export one loop as MP4, WebM or GIF. WebM keeps a transparent background.

A few evenings, in spare time, a little at a time rather than in one stretch.

Andriy Yurchenko, a product designer and design engineer. He made the product decisions, designed and built it on his own, with Claude Code writing most of the code, and takes products for teams from the first decision to a working release.

This article first appeared on ayurchenko.xyz. I am Andriy Yurchenko, a product designer and design engineer. More of my writing is at ayurchenko.xyz/writing.

── more in #ai-tools 4 stories · sorted by recency
── more on @ribbonlab 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-ribbonlab-a…] indexed:0 read:11min 2026-11-13 · —