pro-visu docs
Generators

specimen

A looping type-specimen video from a font file.

specimen renders a looping clip of a typeface set as a fixed number of left-aligned lines of glyphs whose letters and colours change over a composed sequence of pulses, then captures it. The glyph size is derived from lines (the rows fill the top 80% of the frame); the bottom 20% shows the background and the font name. Point it at a font file and give it a name; everything else has a sensible default. No url required.

{
  name: "brand-type",
  generator: "specimen",
  options: {
    font: "public/fonts/YourFont.woff2",
    name: "Your Font",
    template: "sweep",
  },
}

Templates

template loads a named preset; your explicit options still override what it sets.

  • "sweep" — a seamless-looping showcase of even per-character colour sweeps (muted → accent → foreground) on a dark palette chosen so the accent reads.
  • "demo" — a labelled walkthrough of every pulse behaviour (each easing curve, a colour sweep, a mingle) with demo mode on; runs once, no mirror.

Pulses (the storyboard)

A pulse is one beat of the animation: a named span of time during which some fraction of the glyph cells change letter and/or colour. An empty pulse is a hold. The clip length is the sum of the pulse durations (doubled when mirror is on). Compose a sequence to author the whole clip.

Pulse fieldTypeMeaning
namestringHuman label for the beat (readability only). Default "".
durationnumberLength of the beat, in seconds. Required, must be positive.
charsnumber (0–1)Fraction of cells whose glyph changes during the beat (1 = every cell once). Default 0.
colorsnumber (0–1)Fraction of cells whose colour changes during the beat (1 = every cell once). Default 0.
color"foreground" | "muted" | "accent"Target every colour change at this token (a deliberate sweep) instead of a weighted-random pick. Omit for the default scattered recolour.
pacing"even" | "linear" | "ease-in" | "ease-out" | "ease-in-out" | "random"How changes are distributed in time across the beat. Default "even". "linear"/"even" are uniform; "ease-in" front-loads; "ease-out" back-loads; "ease-in-out" bunches at both ends; "random" scatters.

pulses must contain at least one beat. Omit it entirely and a built-in default storyboard (DEFAULT_PULSES — a lively ~10-second outward half: intro hold → first letters → settle → ripple → colour sweep → rest → quick burst → drift → finale → outro hold) is used, which mirrors into a ~20-second seamless loop.

options: {
  font: "public/fonts/YourFont.woff2",
  name: "Your Font",
  pulses: [
    { name: "hold", duration: 0.8 },
    { name: "to accent", duration: 2.2, colors: 1, color: "accent", pacing: "ease-in-out" },
    { name: "glyph drift", duration: 1.4, chars: 0.5 },
  ],
}

Key options

OptionTypeDefaultMeaning
fontstringRequired. Font file (relative or absolute).
namestring""Display name shown bottom-left.
template"demo" | "sweep"Load a preset (overridable).
width / heightnumber1920 / 1080Output frame size.
fpsnumber (≤120)30Frames per second.
durationSecondsnumberderivedOverride the (mirrored) sum of pulse durations.
deviceScaleFactornumber (≤4)1Render scale.
weightnumber (1–1000)820Glyph weight on the variable-font axis.
linesnumber (1–40)3Number of glyph rows. The glyph size is derived so the rows fill the top 80% of the frame.
leadingnumber0.78Line-height of the rows.
blackliststring""Glyphs to exclude (case-insensitive).
characterPoolstringA–Z 0–9 + symbolsOverride the glyph pool (≥2 distinct).
colors{ background, foreground, muted, accent?, label? }see belowColour tokens the glyphs cycle through (override any subset).
colorWeights{ foreground, muted, accent }2 / 2 / 1Relative likelihood of each token on a random recolour.
characterIntensity / colorIntensitynumber1Multiply every pulse's change fractions.
maxLineDriftnumber (0–0.5)0.05Max fraction a line's width may drift as glyphs change — keeps the right edge stable.
mirrorbooleantruePlay the pulses out and back for a seamless loop.
demobooleanfalseOverlay the active pulse's name (to see which beat is playing).
seednumber1Schedule seed — same seed ⇒ identical animation.
crfnumber (0–51)18x264 quality (lower = better/larger).
fileNamestring<name>.mp4Output filename.

seed makes the otherwise-scattered animation deterministic: the same seed always produces the same take. Change it for a different (still reproducible) result.

Colours

colors is the palette the glyphs cycle through (any CSS colour strings). Override any subset; each key falls back independently.

KeyDefaultMeaning
background#eceef1Backdrop behind the glyphs (and the bottom 20% band).
foreground#16181dPrimary glyph colour — the resting majority.
muted#a7adb6Muted/secondary glyph colour.
accentfalls back to backgroundAccent colour for occasional pops. Left unset, accent glyphs blend into the backdrop — so set it (or a template) to make accent reads visible.
labelfalls back to foregroundColour of the font-name label in the bottom corner.

colorWeights sets the relative likelihood of each token on a random (non-targeted) recolour — defaults foreground: 2, muted: 2, accent: 1, so accent is a rare pop. A weight of 0 excludes that token from random recoloring entirely (an explicit pulse color can still target it).

options: {
  font: "public/fonts/YourFont.woff2",
  name: "Your Font",
  colors: {
    background: "#1a1714",
    foreground: "#f6f3ed",
    muted: "#8a7e6f",
    accent: "#b49a77",   // set accent — left unset, accent glyphs blend into the background
  },
  colorWeights: { foreground: 3, muted: 2, accent: 1 }, // keep accent a rare pop
}

Behaviours

  • Width-stable layout. Glyphs are packed into lines left-aligned rows, each filled to a target width using the font's real advance widths (measured in-browser), so every row is about the same length. As glyphs change, swaps are width-compensated — adjacent glyphs change as cancelling pairs, and a per-line width budget caps any residual — so each line's total width, and thus its right edge, stays within maxLineDrift (default 5%) of its initial. The wall shifts pleasantly without the right edge or the line count ever jumping. (A very small characterPool leaves little room to compensate; the layout stays locked but the right edge may breathe a touch more.)
  • Even-coverage picker. Within a pulse, changes are distributed across cells so every glyph is touched once before any repeats. To wash the whole specimen to one colour evenly, set a pulse's colors: 1 with pacing: "even" (and a color target) — that lands exactly one change on each glyph. A fraction below 1 touches that fraction of the cells.
  • Default glyph pool. The specimen draws from ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$%&@#*+= (uppercase A–Z, digits 0–9, and the symbols $ % & @ # * + =). Override it with characterPool (needs ≥2 distinct characters).
  • Blacklist. blacklist removes glyphs from the pool case-insensitively (e.g. "qxz" and "QXZ" both drop Q, X, Z). A blacklist that would remove every glyph is ignored — the pool is never left empty.
  • Mirror. With mirror on (default), the pulses play out and back as a palindrome that ends exactly on the opening frame, so the clip loops seamlessly. It doubles the clip length. Set mirror: false for a one-shot that ends on the last state.

On this page