Building a Generative Jewelry Configurator with Vanilla JS, SVG, and Numerology GemStudio 360, a project by Lochness Paris, built a generative jewelry configurator using vanilla JavaScript, SVG, and numerology. The tool converts a user's name and birth date into eight symbolic gemstones, renders them as procedurally generated SVG beads, and allows fine-tuning before sending the design to a human artisan. The team deliberately avoided frameworks to optimize load time and maintain full control over state management and PWA behavior. published: false description: How GemStudio 360™ turns a name and a birth date into a one-of-a-kind, hand-crafted bracelet — and why we built the rendering engine without a single framework. tags: javascript, svg, pwa, webdev cover image: canonical url: https://lochness-paris.com/configurateur-de-bracelet.html https://lochness-paris.com/configurateur-de-bracelet.html A few months ago we set out to build something that doesn't fit neatly into the usual "product configurator" template: a tool that turns your name and birth date into a personalized gemstone bracelet, lets you fine-tune every single bead by hand, and ends with a real artisan crafting the physical piece. Turn an identity first name, last name, birth date into 8 symbolic gemstones, let the user build a real bracelet around them bead by bead, and hand the final design off to a craftsperson. No accounts, no backend database of users, no checkout — just a fast, installable web app that ends in a personalized request sent to a human artisan. Why no framework? This was a deliberate choice. The whole app — numerology engine, SVG bead renderer, drag-to-build canvas, smart suggestions, cart — is built in vanilla JavaScript, plain CSS, and a single HTML file. Three reasons drove that decision: Load time matters more than developer convenience here. A big chunk of traffic comes from organic/mobile search for terms like "bracelet chemin de vie." Every extra hundred kilobytes of framework runtime is a few more bounces. The DOM tree is genuinely simple. Four tabs, one SVG canvas, a handful of lists. State management doesn't need a library when the state is "an ordered array of beads plus some UI flags." PWA installability and offline behavior are easier to reason about when you fully control the bootstrapping sequence instead of going through a framework's hydration lifecycle. That said — vanilla JS at this scale means you have to be disciplined about state, which brings us to the next point. State management without a framework The entire bracelet is represented as an ordered array of bead objects. Every mutation add, remove, reorder, change size goes through a small set of functions that: update the array, push the previous state onto an undo stack, re-render only the parts of the SVG that changed. That last point matters a lot for performance: with up to ~19 beads rendered as gradient-filled SVG circles, a naive full re-render on every drag event is noticeable on mid-range phones. Re-rendering only the affected bead and recalculating positions trigonometrically around the guide circle keeps things smooth. // Simplified illustration of bead positioning on the guide circle function getBeadPosition index, total, radius, center { const angle = index / total 2 Math.PI - Math.PI / 2; return { x: center.x + radius Math.cos angle , y: center.y + radius Math.sin angle , }; } Undo/redo is just two stacks of array snapshots — simple, but it's the kind of "boring technology" that makes a configurator feel trustworthy. Users mis-click constantly; cheap undo is a UX feature, not a nice-to-have. Generating gemstones instead of photographing them This is the part I find most interesting. With 70+ stone types in the catalog, maintaining a photo library multiple angles, lighting consistency, file size, retina variants… would have been a maintenance nightmare. Instead, every bead is a procedurally generated SVG. Each stone type has a "preset" describing its visual family translucent crystal, opaque mineral, banded stone, metallic finish… , and each individual bead instance gets a random seed that introduces small variations — hue jitter, gradient stop shifts — so that no two beads of the same stone type look perfectly identical, the same way no two real gemstones do. A heavily simplified version of the idea: js function renderBead { id, baseHue, seed } { const rng = seededRandom seed ; const hue = baseHue + rng - 0.5 12; // small natural variation return