Creative tool · SVG systems
Totem
A character builder where independent SVG parts compose, recolor, share, and export without drifting apart.
- Role
- Product engineer
- Stack
- React 19 · TypeScript · Zustand · SVG · Vitest

- 26 parts
- validated before they reach the browser
- 120
- undo snapshots, because regret scales
- 2 formats
- exported from one composition
- 0 servers
- needed to build, save, or share
01
The problem
Character builders look easy right up until one arm floats three pixels off a new body, two gradients share an ID, and the exported SVG forgets every color it had in the browser. The usual fix is a growing pile of per-part exceptions. That pile eventually becomes the product.
Totem takes the opposite bet: artwork should enter through a strict contract, then compose predictably at runtime. The goal was a genuinely useful creative tool—parts, palettes, stats, local saves, durable links, and clean exports—without a server quietly doing the difficult bits offstage.
02
Design
The canvas owns most of the screen, because the character is the point. Editing lives in one compact rail, while undo, randomize, share, save, and export stay in reach above the work instead of hiding behind a kebab menu wearing a tiny fake moustache.
Parts, rules, and color all use ordinary tabs, radios, and text inputs. The interface remains fast to scan and fully keyboard-operable, while the decorative grid, glow, and orbit marks give the builder enough occult machinery to feel like a toy rather than an asset-management form.


03
Architecture
- 01Authored SVG
- 02Build-time contract
- 03Typed manifest
- 04Canvas, URL, and export
Each loose SVG passes through a compiler that rejects hardcoded paint, unsafe features, bad coordinate spaces, missing anchors, and dangling references. It resolves anchor coordinates, strips authoring markers, namespaces every identifier, and emits a typed manifest. The browser receives finished geometry instead of a small archaeology assignment.
Bodies and limbs agree on named attachment points; held items chain through the selected hand. Recoloring uses four CSS custom properties with derived light and dark tokens, and export resolves those same tokens to literal values. One deterministic character configuration therefore drives the canvas, undo history, local gallery, compact share URL, standalone SVG, and transparent PNG.
04
Interesting decisions
- 01
Make bad art fail the build
An SVG can look correct alone while carrying hardcoded colors, duplicate IDs, or anchors that break only after composition.
- Choice
- Compile every part through a contract that validates, normalizes, namespaces, and resolves its geometry before shipping.
- Tradeoff
- Asset authors follow stricter rules, but the runtime never has to improvise around malformed input.
- 02
Compose by agreement, not exception
A matrix of body-specific offsets grows combinatorially and makes every new part a regression tour.
- Choice
- Connect parts through named anchors and pure transforms, mirroring the authored right limb for the left side.
- Tradeoff
- The authoring contract is less forgiving; in return, every valid arm fits every valid body without a bespoke nudge.
- 03
Keep shared characters boringly durable
Encoding array positions makes an old share link silently change character when the part list is reordered.
- Choice
- Use a versioned URL codec and an append-only index registry where removed parts leave tombstones.
- Tradeoff
- The registry never becomes perfectly tidy, which is cheaper than explaining why someone’s wizard became a haunted turnip.
- 04
Pan and zoom outside React
Pointer moves can arrive faster than a component tree should reasonably re-render.
- Choice
- Write one SVG wrapper transform per animation frame and keep React subscribed only to the scale readout.
- Tradeoff
- Viewport state lives beside the component model, but dragging and pinch zoom stay smooth and character edits remain ordinary React state.
05
Accessibility
- Every part, palette, and attribute choice is exposed through native tabs, radio groups, buttons, and labelled inputs; the picture is not the only source of truth.
- The complete editor works by keyboard, focus stays visible against the dark shell, and disabled stat choices communicate the budget before submission.
- Zoom has named controls and a reset readout, while undo, redo, randomize, sharing, saving, and every export size have explicit accessible names.
- Reduced-motion preferences collapse nonessential transitions; the character, controls, and feedback do not depend on animation appearing at all.
06
What I'd do differently
The strongest decision was making the part contract a product feature rather than a build chore. Once the assets agree on anchors, paint, IDs, and stable indices, the canvas, sharing, and export paths become pleasantly dull—and pleasantly dull systems are the ones that survive more inventory.
A next pass would add a visual-regression matrix over representative part combinations and turn the tiny part library into a documented contributor workflow. I would also add an export preview for people who want reassurance before downloading four sizes of the same tiny forest bureaucrat.
Have a similar project?