Study 02 · tristan.design

A front door that is also an instrument

The landing page of tristan.design opens on a full-screen generative visual synthesizer instead of a portfolio grid. Everything on it is playable, and one of the choices a visitor makes there follows them through the rest of the site. These are the notes on how it works.

Live tristan.design Build single-file bundle, 34 assets Runtime vanilla JS, 2D canvas Dependencies none
Live — click a letter, drag the background Open full size ↗
01 — The premise

The toy is the portfolio piece

Most design portfolios lead with a grid of work. This one leads with a thing you can play with, and puts the work underneath as a short list of names.

The reasoning is that a portfolio has about two seconds to prove that the person who made it can build something, and a generative visual engine running at full bleed proves it faster than a screenshot of a poster does. The work is one scroll away and clearly labelled; the front door is the argument for opening it.

The page is two screens. The first is the synthesizer and the wordmark. The second is a warm paper panel holding the project links, an about page and the contact route.

02 — The visual engine

Sixteen modes on one canvas

An original EYESY-style engine drives a single <canvas> behind the wordmark. Each mode is a draw function operating on the same five-parameter state, so switching modes is instant and the parameters carry across — a shape you have dialled in stays dialled in when you move from Lava Lamp to Chomp.

Bubble Wrap01 · outlined circle grid
Neon Nostalgia02 · column polygons
Prism Panic03 · patchwork polygons
Groove Juice04 · uniform polygons
Slime Funk Meltdown05 · hertsi
Hypno Waffle06 · hypnogrid
Disco Maze07 · mirrored grid
Portal Punch08 · radiating square
Glitter Bomb09 · sequins
Lava Lamp10 · flow-field drift
Wingding Rave11 · font patterns
Spirograph Sludge12 · circle scope
Goo Tube13 · gradient column
Chomp14 · sound jaws
Bouncy Castle15 · bar bounce
Chaos Engine16 · randumbizer

What the canvas is doing

  • Frames are throttled to roughly 30fps — the draw loop returns early if less than 32ms has passed. The modes are dense enough that the extra frames cost battery without reading as smoother.
  • Device pixel ratio is capped at 1.25 on viewports wider than 720px and 2 below it. A phone gets the sharpness; a 5K display does not get asked to fill 15 million pixels thirty times a second.
  • Trails are a mode of the clear step rather than a post effect: switching persistence off repaints the ground each frame, switching it on lets the previous frame bleed through.
03 — The control surface

A sheet on a phone, a panel on a desktop

The controls live in one panel with four tabs. On mobile it behaves as a draggable bottom sheet with a peek detent; on desktop it floats. It stays out of the way until asked for — the page is fully playable without ever opening it.

The four tabs
TabControlsNotes
ModePrevious / next, plus a full selectTapping the artwork itself also jumps to a random mode
ShapeFive drag knobs, labelled ♥ ✿ ★ ☯ ☻The same five parameters mean something different in every mode, which is the point — you dial rather than configure
ColorForeground and background steppers with live swatchesTwo palettes: a free hue walk, or a constrained brand set
EffectFlash, Shuffle, Trails, Palette, MusicFlash fires a one-shot trigger the modes respond to

Keyboard

The panel is operable without a pointer: ← → step the mode, space flashes, P toggles trails, R shuffles, Esc closes. The knobs are real role="slider" elements with arrow-key support, not divs with drag handlers bolted on.

Music

The Music button asks for system or tab audio via getDisplayMedia and falls back to the microphone. The stream is routed through an AudioContext analyser at fftSize 256, and the level it reads is fed into the same trigger the Flash button fires — so every mode is already audio-reactive without a single mode-specific line of audio code.

Teaching it

A help card explains the four gestures in the page's own voice: swipe the background sideways for a new pattern, tap it for a surprise, click a letter in the name for a new face, open the cloud for the full set of controls. It is shown once and remembered.

04 — The wordmark

Seven letters, eight faces, one control surface

The word tristan sitting over the canvas is not an image. Every letter is its own element, and clicking one advances it to the next typeface in a cycle of eight.

The face cycle
#Face#Face
0Shrikhand4Killuminati
1WooowLight5Okiedoko
2Bigsock6Boardslide
3Mornes7Kaushan Script

Because seven letters each hold one of eight faces independently, the wordmark has 8⁷ — just over two million — possible states. Every one of them is a legible rendering of the same word.

The details that make it feel like an object

  • Each press fires a jolt: a small randomised translate, rotate and scale on that letter alone, on a stepped easing curve so it snaps rather than glides.
  • A burst of particles comes off the letter, and each letter throws its own glyph — t throws lightning, r throws hearts, n throws moons.
  • Glyph bounds are measured with SVG getBBox rather than canvas measureText. These display faces have wildly different ascents, and canvas measurement silently falls back to a substitute font and under-reports them, which made the layout jump. Reading the real loaded outline fixed it.
05 — Carry-over

A choice made on the front door follows you

This is the part that ties the site together. The faces you pick on the landing page are not decoration on that page — they are site-wide state.

01
A visitor clicks a letter. The face index for that letter changes.
02
The seven indices are written to localStorage as a single array.
03
A custom event fires for anything listening in the same document — the wordmark at the foot of the page updates immediately.
04
Every other page carries a small sync script. On load it reads the array and applies the faces; it also listens for the browser's storage event, so a change made in one tab lands in the others live.
05
Open a case study and the wordmark in its nav is already wearing the faces you chose.

Why it matters. It is a two-million-state personalisation with no account, no cookie banner and no server — about forty lines of code total. A visitor who plays with the name on the front door finds their own version of it waiting on every page after, and most people notice it exactly once, which is the right number of times.

06 — Downstream

The same letters run the panel below

On the lower panel, the same seven numbers are put to a second use. Each project link takes its underline treatment from the matching letter — link one from letter one, and so on — and the last letter sets the style of every glyph in the list.

06px solid
11.5px hairline
27px double
35px dotted
45px dashed
5scallop
6zigzag
7wave

The last three are repeating inline SVG rather than border styles, because CSS has no scalloped or wavy border and a background image tiled along the bottom edge costs nothing.

Eight glyph treatments

The seventh letter switches the whole list's marks between fill and stroke weights, dashes, and rotations:

0
1
2
3
4
5
6
7

Nothing announces any of this. The panel simply looks slightly different depending on what you did upstairs.

07 — State

What is remembered, and where

Everything persists in localStorage. There is no server, no session and no analytics attached to any of it.

Storage keys
KeyHolds
tristan-lettersThe seven face indices for the wordmark. The one key read by every other page.
tristan-eyesy-v6Synth state — mode, the five shape parameters, foreground and background positions, trails, palette.
tristan-help-seen-v1Whether the how-to-play card has been shown.
tristan-touched-v4Whether the visitor has interacted with the canvas, used to stop nudging them.
tristan-touched-panel-v1The same, for the control panel.
tristan-ctrl-miniPanel size preference.
tristan-ctrl-usedWhether the panel has been opened before.

Every read is wrapped in a try/catch and every one has a working default, so private browsing, cleared storage and a blocked storage API all degrade to a page that simply starts fresh.

08 — Motion, access, weight

The parts that are easy to skip

Reduced motion

The page respects prefers-reduced-motion in nineteen places. Under it, letter jolts and particle bursts do not fire, the scroll-reveal on the project links resolves immediately rather than sliding in, and the decorative line pulse is removed outright. The synth still runs — it is the content, not an animation — but nothing jumps at anyone.

Keyboard and screen readers

The wordmark carries an aria-label of the plain word, so it reads as "tristan" rather than seven separate letters. The shape knobs are sliders with proper value ranges. Every control has a visible focus ring, and the whole synth can be driven from the keyboard alone.

Weight

Nine display faces are self-hosted for the wordmark, which is expensive — until you notice they only ever set one word. Every one of them is subset down to the fourteen characters of tristan in both cases and re-encoded as WOFF2, taking the page from 4.17 MB to 1.13 MB with no visible change. The body face is left whole, because it has real text to set.

The trade this documents. A generative front door is a risk: it delays the work by one scroll and it asks a visitor to be curious. The mitigations are all in this page — it is fast, it is playable without instructions, it works with the keyboard, it calms down if you have asked your system to calm things down, and it remembers what you did.