Deadlock is by far my favorite game of the decade for its unique and distinctive art direction, which inspired me to create this project.
An experimental typography workshop for industrial, punk, underground wordmarks. Type a short name, choose a hand, and see an SVG composition update instantly.
Built with Svelte 5, Vite, TypeScript 7, and Bun, with a small lexer/parser and vector layout engine. Inspired by the individual hero wordmarks in Valve’s Deadlock.
- Eleven distinct lettering packs: Rat King, Nurse Harrow, Baba, Solomon, Venator, Celeste, Violet, Graves, Viscous, Paradox, and Paige.
- Instant previews for names of up to 24 letters or digits.
- An R leg that adapts to the width of its word.
- Crowns, decorative I dots, alternate letters, uneven heights, spacing, and ink controls.
- Global switches and per-letter overrides, from the preview or letter selector.
- Optional inline syntax and visible parser output.
- Side-by-side reference comparison and layout guides.
- Standalone SVG export with embedded outlines; exported lettering needs no installed fonts.
Requires Bun 1.4.2 (pinned in .bun-version and package.json). Install it from bun.sh.
bun install --frozen-lockfile
bun run devOpen the local URL printed by Vite. bun.lock pins dependencies. TypeScript 7.0.2 is installed as @typescript/native; Svelte diagnostics also require the TypeScript 6.0.3 compatibility API. bun run check uses TypeScript 7 for both Svelte and standalone TypeScript checks.
bun run format # Apply formatting
bun run check # Formatting, typechecking, and lint
bun test # Parser, layout rules, and override precedence
bun run build # Static production build in dist/The production output can be hosted by any static web server. Rendering runs entirely in the browser. Interface fonts currently load from Google Fonts; the lettering engine does not make network requests.
.github/workflows/deploy.yml runs on every push to main, or manually from the Actions tab on main:
- Set up the pinned Bun version and install with
bun install --frozen-lockfile. - Run
bun run check(formatting, types, lint), thenbun test. - Build the app for the GitHub Pages base path and upload
dist/. - Deploy to GitHub Pages only after all earlier steps pass.
In the repository's Settings → Pages → Build and deployment, select GitHub Actions as the source. Commit the workflow and app changes, then push to main. In Actions → Check and deploy, verify that both jobs succeed; the deployment job links to the published site. The default URL is hmrguez.github.io/underworld-lettering/.
The workflow uses GitHub's automatic token; no deployment secret is required. Reference artwork uses Vite's base URL so it also loads from the repository subpath. To verify that build locally:
bun run build -- --base /underworld-lettering/
bun --bun vite preview --host 127.0.0.1 --base /underworld-lettering/Open the preview URL with /underworld-lettering/ appended and enable Reference comparison to check artwork loading.
Type plain text or attach settings immediately after a letter:
R[swash=word]AT KI[dot=crown]NG
R[swash=off]AVEN
I[dot=star]
B[variant=alt]ABA
S[plate=on]OL[rook=off]OMON
V[initial=off]ENATOR
AV[variant=alt]A
C[initial=off,swash=off]ELESTE
S[initial=on,ornament=off]TAR
G[fracture=off]RAV[fracture=on]ES
VISCO[bubble=off]US
B[bubble=on]UBBLE
S[variant=alt]OFT
P[initial=off]AIGE
P[frame=off]AIGE
R[initial=on,frame=on]AVEN
| Setting | Values |
|---|---|
swash |
auto, word, off |
dot |
auto, crown, star, off |
variant |
auto, base, alt |
ornament |
auto, on, off |
plate |
auto, on, off |
initial |
auto, on, off |
frame |
auto, on, off (Paige) |
bubble |
auto, on, off (Viscous) |
fracture |
auto, on, off (Graves) |
rook |
auto, on, off |
Multiple settings can share a bracket, separated by commas. Settings apply when the style and letter support them. Interface overrides take precedence over inline settings; inline settings take precedence over automatic behavior. Editing the source clears interface overrides.
Input supports A–Z, digits, spaces, hyphens, and apostrophes. Lowercase is normalized to uppercase. Invalid syntax produces a diagnostic while valid letters continue to render.
Angular brushwork, fractured edges, an adaptive R swash, and a crown. R, A, T, K, I, N, and G use reference-derived vector outlines. Other capitals use inferred brush designs; digits and punctuation use fallback outlines.
Tall, high-contrast serifs, two-line compositions for multiple words, and an R flourish crossing into the lower line. N, U, R, S, E, H, A, O, and W come from the reference; missing glyphs use Cormorant Garamond outlines.
Decorated slab letters, folk ornaments, and alternate A/B shapes. A and B use reference-derived outlines. Other glyphs use Rye outlines with additional decorative treatment.
Tall, narrow serif capitals and independent slanted plates with transparent reversed ink. Plates default to even positions (2, 4, 6), with an odd/even phase switch restarting per word. The rook has its own switch and per-letter overrides; automatic placement uses the first L in each word, or its middle letter when no L exists. When its letter has a plate, the rook rises above that plate.
S, O, L, M, N and the rook use extracted reference contours, including three O variants. Other capitals are custom inferred geometry with heavy stems, thin connectors and wedge serifs. Digits use compressed Cormorant Garamond fallback outlines. Spacing and plates are reconstructed; unseen letters are not claimed as exact.
Heavy blackletter with angular shoulders, broken bowls, narrow counters and pointed terminals. V, E, N, A, T, O and R retain the reference contours and native wear. The hooked V is contextual at word starts; initial=on|off|auto controls it. variant=base chooses an inferred compact v, while variant=alt forces the reference V anywhere. Other inferred letters offer pointed-shoulder alternates; the remaining source letters have a single evidenced form.
Diamonds and XLVIII have independent switches. The inscription defaults off and is reference-specific, even for the Venator preset. ornament=on|off|auto on the first letter controls the diamond pair. Spacing uses measured ink profiles and protects an 8-unit minimum clearance. Missing letters, numerals, punctuation and the compact v are custom inferred outlines; no additional font is used.
An oversized decorated initial contrasts with narrow, curved serif letters on an ascending baseline. The first letter of the first word is decorated automatically; initial=on|off|auto can force or suppress it on any capital. C keeps the reference contour; other initials enlarge their own shapes and use inferred decoration. Turning the initial off selects an inferred compact C.
Stars and interior details have a global switch and per-letter ornament overrides. Each word gets an independent double underline; swash=word|off|auto on its first letter controls it, with a reach slider. The ribbon bodies adapt to composed width while preserving vertical thickness and fixed terminal proportions; short words use shallower curves. Interface overrides win over inline settings and clear when text changes.
C, E, L, S and T, three E contours, the source interior details, dots, stars and ribbon templates are extracted from the verified Celeste wordmark. Missing letters, digits, punctuation, compact C and non-C initial treatments are inferred specifically for this hand, without a new font. The unseen alphabet is not an exact reconstruction; spacing, multiple-word layout, star placement and adaptive underlines are interpretations.
source text
↓
lexer → tokens and source positions
↓
parser → words, glyphs, modifiers, diagnostics
↓
style pack → outlines, metrics, alternate shapes
↓
layout + contextual rules → flourishes and ornaments
↓
SVG → instant preview / export
src/lib/parser.ts: lexer, parser, validation, and modifiers.src/lib/engine.ts: style selection, layout, contextual flourishes, and SVG generation.src/lib/reference-paths.json: extracted reference paths.src/lib/inferred-rat-paths.json: inferred angular brush capitals.src/lib/solomon.tsandsrc/lib/solomon-paths.json: Solomon geometry, plates, masks and rook rules.scripts/extract-solomon.py: reproducible reference contour extraction (Python standard library).src/lib/venator.ts,src/lib/venator-paths.jsonandscripts/extract-venator.py: Venator contours, inferred geometry, measured ink profiles and independent ornaments.src/lib/violet.ts,src/lib/violet-paths.jsonandscripts/extract-violet.py: split Violet outlines, inferred pressure ribbons, contextual shaping and controlled script joins.src/lib/celeste.ts,src/lib/celeste-paths.jsonandscripts/extract-celeste.py: separate Celeste contours, custom inferred glyphs, contextual initials and adaptive ribbon curves.src/lib/paradox.ts,src/lib/paradox-paths.jsonandscripts/extract-paradox.py: source geometric capitals, custom inferred outlines, contextual A forms and selective transparent counters.src/lib/viscous.ts,src/lib/viscous-paths.jsonandscripts/extract-viscous.py: extracted soft source forms, custom inferred outlines and fitted transparent bubble holes.src/lib/graves.ts,src/lib/graves-paths.jsonandscripts/extract-graves.py: reference fragments, reconstructed intact envelopes, inferred heavy serifs and ink-fitted fractures.src/lib/paige.ts,src/lib/paige-paths.jsonandscripts/extract-paige.py: source blackletter outlines, a custom inferred alphabet, contextual initials and an independent adaptive frame.src/lib/fallback-paths.json: precomputed fallback font outlines.scripts/design-rat-glyphs.cjs: regenerates the inferred Rat King capitals.src/App.svelte: editor, preview, settings, and export.tests/engine.test.ts: parsing and rendering behavior checks.
The renderer composes glyph geometry rather than swapping in a finished name image. Flourishes respond to the surrounding word; the same engine renders presets and arbitrary input.
A dramatic brush V followed by connected, forward-slanting lowercase forms. The parser keeps uppercase source characters; Violet shapes the first alphabetic letter of each word as a capital and the remaining letters as lowercase. Selection IDs and source offsets stay intact. Hyphens, apostrophes, digits and word breaks interrupt connections.
The Extended strokes switch controls the V treatment, t crossbar and contextual word endings; Swash reach changes crossbar/end-stroke reach. Subtle brush wear adds selective transparent scratches. Native reference wear remains in the source contours. Per-letter initial controls capital/lowercase form, swash controls extension, ornament controls extra wear, and variant=base disengages adjacent joins (alt restores contextual eligibility). Interface overrides take priority over inline settings, including explicit automatic values; text edits clear them.
Default spacing connects reachable shoulders using measured entry/exit points and pair-dependent curves. Tight spacing is clamped; spacing at +50 and above disengages joins and uses separate entries/endings. The original V remains deliberately separate from its following i. Extended t crossbars can pass over neighboring ascenders; the model is controlled script composition rather than a general collision solver.
Reference-derived: V, lowercase i, o, l, e, t, their visible pressure variation and dry-brush contours. The connected source outline is split at reconstructed shoulder cuts, with an independent t crossbar. All other capitals/lowercase, numerals, punctuation, joins and contextual endings are inferred. The shortened V is also inferred. These cleaner pressure-based outlines interpret the visible hand; they do not reconstruct Valve's unseen alphabet. No new font is bundled.
Heavy, compact serif masses with angular counters and unequal structural breaks. Structural fractures controls automatic cracks; Fracture intensity ranges from 40–100% and defaults to the inspected reference width. Per-letter Automatic / Intact / Fractured uses the narrow syntax fracture=auto|off|on. Intact overrides remove structural breaks independently of the global switch; explicit fractured overrides work with the switch off. Existing precedence and text-edit reset rules apply.
G, R, A, V, E and S retain visible source fragments and first-occurrence fractures. Repeated letters use three deterministic patterns, selected by character and occurrence and fitted to connected ink runs. Cut widths are capped to protect stems and counters. Cracks subtract through transparent SVG masks and export correctly over arbitrary backgrounds. No font is needed.
Intact envelopes, the shared R/A, V/E and E/S boundaries, spacing and repeat patterns are reconstructed. Every other capital, all digits and punctuation are custom inferred heavy serif geometry, distinct from Baba's patterned slabs. The unseen alphabet is an interpretation, not an exact Valve reconstruction. Native small contour nicks remain separate from structural fractures; no random surface texture is added.
Thick, soft, uneven masses with a sagging V, pinched I, open C, two unequal S forms and three bubble cutouts inside a solid O. V, I, S, C, O and U retain extracted source outlines and native heights. S forms alternate by occurrence; variant=base|alt|auto selects them directly. Unseen capitals, digits and punctuation are custom swollen outlines made from explicit curved skeletons, not a rounded fallback font. Inferred capitals except Q also offer a restrained pressure alternate.
Bubble cutouts enables the three native O holes globally. bubble=off removes them locally; bubble=on works with the global switch off and adds a small fitted hole on other characters. Extra holes are inferred and kept within thick ink. Transparent masks work over any export background. Interface overrides take precedence, including explicit automatic values; editing text clears them. Arbitrary spacing is reconstructed with a six-unit minimum ink-bounds gap. Unseen forms are an interpretation, not Valve's complete alphabet.
Massive geometric capitals with compact spacing and rounded hourglass counters. P, A (two forms), R, D, O and X retain extracted outlines from the inspected PARADOX wordmark. The two A forms alternate by occurrence; variant=base|alt|auto selects either. P, R and D stay solid automatically, matching the source.
Hourglass counters controls A/O and inferred Q/zero counters. Per-letter Automatic / Solid / Hourglass (counter=auto|off|on) can suppress them or explicitly add a fitted inferred counter to B, D, P, R, Q, 0, 6, 8 or 9. Open and narrow forms remain solid. Interface overrides win over inline values, including explicit automatic settings; text edits clear overrides. Tracking preserves six units of clearance between sampled ink profiles while retaining the source preset's native bearings.
All other capitals, digits, punctuation and counters outside A/O are custom inferred geometry. No font was added, and unseen letters are not exact Valve reconstructions. Transparent masks and embedded outlines make exports independent of installed fonts and background colors.
Dense angular blackletter with broad stems, oblique counters, folded terminals and an oversized P inside a partial rectangular frame. P and lowercase-form a/i/g/e retain extracted reference contours, including the angled i dot and descending g. All other letters, digits, punctuation and compact p are custom inferred geometry. This hand has its own proportions and outlines, distinct from Venator; no blackletter font was substituted.
Enlarged initial and Open initial frame are independent switches. Both automatically apply to the first alphabetic letter of the name, skipping digits and punctuation. Other initials enlarge their own shapes with an explicitly inferred treatment. Per-letter initial=auto|on|off and frame=auto|on|off can force or suppress either treatment anywhere. Interface overrides win over inline settings, including explicit Automatic; editing text clears them.
The frame adapts to the selected glyph and opens around neighboring dots, descenders and forced initials while preserving border thickness. Source pairs retain their native folded-foot contacts; arbitrary pairs protect sampled ink clearance. Arbitrary-name spacing and frame adaptation are interpretations, not a recovered complete alphabet. Exported glyphs and the independent frame are filled SVG outlines with transparent counters.
This is a personal v1 experiment, not a complete reconstructed font family. Original hero names are close to the references. Letters absent from those references are inferred, and the interface labels them. Spacing and some ornament placement are reconstructed. There is no manual Bézier editor, font uploader, or OpenType font export.
-
Paige artwork: Valve, isolated bookworm wordmark linked from DeadlockSkins.gg’s Paige page. Actual vector contents were visually inspected; the asset spells Paige. See visual verification for extraction, inferred forms and export evidence.
-
Paradox artwork: Valve, isolated chrono wordmark linked from DeadlockSkins.gg’s Paradox page. Actual vector contents were inspected; the asset spells PARADOX. See visual verification for extraction, inference and export evidence.
-
Viscous artwork: Valve, isolated Viscous wordmark linked from DeadlockSkins.gg’s Viscous page. Actual vector contents were visually inspected. Extraction and inference limits are recorded in visual verification.
-
Graves artwork: Valve, official Graves poster on Old Gods, New Blood; isolated necro wordmark via DeadlockSkins.gg. Actual poster and vector contents were inspected. See visual verification for reconstruction boundaries and evidence.
-
Violet artwork: Valve, official Violet poster on City Never Sleeps; isolated artist wordmark via DeadlockSkins.gg. Actual image contents were verified. See visual verification for provenance and evidence.
-
Visual direction and original hero lettering: Valve, Deadlock — City Never Sleeps.
-
Celeste artwork: Valve, official Celeste poster on Old Gods, New Blood; isolated unicorn wordmark via DeadlockSkins.gg. Extraction and fidelity limits are recorded in visual verification.
-
Venator artwork: Valve, Old Gods, New Blood; isolated priest wordmark via DeadlockSkins.gg. No commercial blackletter font is bundled.
-
Solomon asset verification and regression results: visual verification. The official asset filenames currently mismatch their artwork; Solomon is visible in
hero_baba.webp?v=2, whilehero_solomon.webpshows Deadman Danny. -
Isolated reference SVGs: DeadlockSkins.gg, stored in
public/references/. -
Fallback outline sources: Cormorant Garamond, Rye, and Sedgwick Ave. SIL Open Font License texts are included in
public/references/. -
Interface fonts: Barlow Condensed and IBM Plex Mono via Google Fonts.
This project is unofficial and is not affiliated with Valve. Bundled reference artwork and reference-derived glyphs remain third-party material; this repository does not claim ownership of them or grant a license to them. No blanket license is applied to the repository.











