DEV Community

Cover image for Building Ribbonlab, a 3D Animated Background Generator
Andriy Yurchenko
Andriy Yurchenko

Posted on Originally published at ayurchenko.xyz

Building Ribbonlab, a 3D Animated Background Generator

Originally published on ayurchenko.xyz.

The live ribbon in the Ribbonlab editor

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.

How the work went

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 Ribbonlab editor: a green and yellow fibre ribbon across a white page, the dark tuning panel on the right with the Aurora palette and five colors, the shortcut bar at the bottom

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

Textures

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.

A blue ribbon close up with thick wavy bands of light across it, like muscle fibres

A blue silk ribbon close up with fine straight threads running along a smooth surface

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.

The first fibre optic texture on a violet ribbon: even dark and light stripes over the whole band

The shipped fibre optic texture on the same violet ribbon: soft glowing strands of uneven width over a bright surface

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.

A ribbon in the Mercury texture on the Sakura palette, reflecting in grey and white

The same Mercury ribbon reflecting in the palette’s pink

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

Close-up of a fibre optic ribbon in green, teal and yellow strands

Close-up of a lavender ribbon with fine glitter

Close-up of a gold ribbon in brushed metal

Close-up of a coral ribbon with a pearl finish

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

Shape

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

A blue silk ribbon with the starting shape values: a tight twist that turns the band edge-on in the middle

The same ribbon with the tuned values: a wide band that sweeps diagonally across the frame

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 Ribbonlab panel with the Shape folder open: Form Single or Folded, Width, Length, Thickness, three twists, three falloffs and Wave, beside a violet ribbon

The Shape folder on the default ribbon.

Settings

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.

A white Cobweb ribbon whose shaded folds come out blue

The same white ribbon with neutral grey shading

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.

Prototype: a Looks window over the dimmed editor with ten rendered ribbons in two rows

Prototype: a stage with the preset name Violet in large type and arrows under it

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.

The Ribbonlab export window on the Code tab: HTML, React and URL options, background Hide or Show, a code preview with an iframe snippet and a Copy code button

Export, Code tab. One snippet, with a note on where it goes in each site builder.

Speed

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

Why this matters for your product

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.

Questions

Is Ribbonlab free?

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

How do I add an animated 3D background to my website?

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.

Which textures does Ribbonlab have?

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.

Can I export a seamless looping video?

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.

How long did Ribbonlab take to build?

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

Who made Ribbonlab?

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.

Top comments (0)