MENAGERIE BOOT

how it is drawn

MENAGERIE is a board of thirty-odd creatures — human, cat, dog and imp, each breathing on its own clock, drawn as if by hand. A board is rolled fresh each time; one you keep, or a creature you make, is a file of JSON. It runs in the browser with no build step: the board is here, the code is here.

This page is the medium — what they are drawn with, on three axes: the goofy outline, the goofy material and the goofy fur, plus the goofy type the name screen's card is written in, the goofy letters, the shapes and colours. No textures and no brush images; the hand comes from rules. It is all three.js, every stroke and fill a triangle.

Every figure below is drawn live by the code that draws the creatures — the same src/stroke.js and src/medium/, at the board's own scale, so nothing here can drift from what the board does. INK BOIL (or I) sets them boiling; a reload draws them all in another hand.

THE GOOFY OUTLINE

Every line on a creature is a goofy outline — a kind from GOOFY_OUTLINES in src/medium/outlines.js. A part names no kind, no line function and no width. It names its line's role: ink.contour(path, { color, size }) closes a shape and ink.line(…) runs open, down to a dot — with at most a size on it. One switch, BOARD_LINES, says which kind draws each role; change a name there and every line of that role changes.

A kind is named the pen, then the hold. One pen is on the table, the pencil, in three holds: STROKE (once, full width — mass) · SLINE (thin, the pen lifts — detail) · BROKEN (three times over itself — contour). One line each, the same path:

A kind also draws at a size — S · M · L, each kind naming its own three: STROKE 0.004 · 0.007 · 0.012, SLINE 0.002 · 0.003 · 0.005, BROKEN 0.0035 · 0.006 · 0.011. A ladder is the kind's own rather than a multiple of one width, because a hairline blown up to STROKE's L stops being a hairline. A size is the only way a width is said — no multiplier on top, so a width that is not on a ladder cannot be drawn. Most lines are M, fine detail S, and two lines on the whole board L. The hold does not change with the size: SLINE still lifts at L, BROKEN still stacks its ghosts at S. AGAIN redraws in another hand.

in use on the board

The two rows read the switch live: 45 contours and 119 open lines, at three widths and no others, so no creature wears a two-line ear beside a one-line eye. What the table does not cover: the two bands (a hat's colour laid as a thick stroke, a fill in disguise), the emoji glyphs, and the marks a goofy material and the goofy fur lay down — the medium's own, not a creature's lines. All of them the same pencil.

contour — 45 of them: the head and bodies, the ears, hats and hands, the eyes and eyewear, the nose, the mouth's parts. line — 119: lids, brows and lashes, whiskers, limbs and feet, hair strands, horns, the floor, and every dot and dash. S for the finest, M for the rest, L for an imp's horn and a stub tail.

the pencil

A stroke is no line — WebGL draws none thick enough. It is a chain of quads: two samples make four corners, each pushed to both sides along its normal by the half width, cut into two triangles. The pencil takes one normal per point and hands it to the quads on both sides, so a corner closes; on a closed line the last quad joins the first, which is the seam it does not have.

a normal per point — the corners close
the quads — every habit off, magnified far past the board's scale. Each hairline is one triangle edge, read back out of the triangles the pen just wrote: rungs where two quads meet, diagonals where each is cut in two.

PENCIL_STROKE · PENCIL_SLINE · PENCIL_BROKEN — one pencil, three holds. Its sines run per world length rather than per line, so the wander comes out the same size wherever it lands; a closed line snaps them to whole cycles and never overshoots, which is the rest of why it has no seam. Every number is in PENCIL in src/stroke.js. The ink is opaque, which is what lets the creature in front hide the one behind.

PENCIL_STROKE is full width, laid once — every line on the board. PENCIL_SLINE is a hairline whose pen lifts: the skips are in world units along the path as handed over, so the kind breaks in the same places at every size, and none falls near an end or on a line too short to spare it. PENCIL_BROKEN stacks the ghost: two thinner passes, one at a fifth of the ink and one at half, each slipping to one side or the other, with the line laid last on top. SLINE and BROKEN are built and on nothing — the switch says what the board draws.

A dot or a dash needs no kind of its own: the pencil keeps the ends of anything shorter than 0.05 and sheds nothing there, because an overshoot would run a dot half again to twice as long and a crumb is the size of the dot.

One habit per row, each row the row above plus one. The sixth is the whole pencil — that is PENCIL_STROKE; the seventh adds the ghost, and stacking that is PENCIL_BROKEN.

wobble 0.451.01.9
every individual has a different hand — proportions.wobble multiplies the jitter per creature. On one constant the whole board falls into the same handwriting.

THE GOOFY MATERIAL

A goofy material is what a surface is made of, the way a 3D material is — how its area is filled, as channels. (Goofy, like the outline and the fur, to keep it apart from the GPU material underneath.) There are two channels and they paint the same thing, the colour of the surface:

base — the fill-up: always opaque, in the part's colour.
texture — hatch, scratch, dab, speckle or wash, laid over the base and clipped to the contour.
pattern — the creature's own mark (stripes, dots, spots, hatching), clipped to the contour and laid last, over the material: it is something on the animal, not part of the surface, and under a hatched or dusted texture it was being buried.

That is the whole of it — no opacity channel, and grain is the sheet's job, not a material's. A part names a material and hands over the path and the colour (fills.paint(path, "FLAT", { color, offset })); it never picks a technique, and every tone the texture adds is a shade of the part's colour.

A material is three things, and the editor's MATERIALS card lays them out in that order. A texture — which of the six techniques fills the area: GRAPHITE, CHARCOAL, OIL, INK, WATERCOLOUR or MARKER (the material slot). A density — which of the five value steps it is laid at, low to high (the density slot). A colour — the box the surface is painted from. FLAT, the fill-up with nothing laid on it, is not a texture a creature can be made of: it is what the whites of the eyes are filled with.

A creature wears two materials. The main one covers the head's side — the head, the ears, the horns, the hair, the hat, the muzzle and the face — in the skin box's colour. The body's covers the torso, the arms and hands, the sleeves, the legs and boots, and the tail, in the cloth box's colour, with a texture and a density of its own (bodyMaterial, bodyDensity). A face and a torso are two surfaces, and one hand may reach for a second tool, or press differently, between them: the roll hands the body the main's texture on three creatures in four and the main's density on four in five, and its own on the rest. pattern rides along too.

the five textures, at the five densities

Every texture in GOOFY_MATERIALS gets a row of shader balls — the same ball at the five density steps, light · stipple · scribble · hatch · black from low to high, named for the way graphite makes each. A step is an amount of the medium — how many marks a material lays — and the ground is the part's colour at every step (it used to be pulled toward the technique's own tone as well, and a creature's palette colour darkened as its density rose). The step is the creature's density slot — one of the five, picked by the roll, so every step turns up on every species; the colour decides the marks' tone, the step how many.

A mark has to be lighter than what it is drawn on, so on a dark ground every technique turns around — the lower row of each pair.

A mark is a light. Ink's scratches, graphite's rules and oil's paint are all drawn in the same tone — the ground tinted, its own hue carried toward white — so they come up out of the surface. Every tone stays in the part's own family: a blue part gets lighter and deeper blues, where mixing toward the light ink to lighten turned its marks grey. Charcoal alone keeps dark marks; one technique going the other way is what keeps the four apart.

And no mark is ever white. A lighter tone is the colour tinted — its own hue carried toward white — never a multiply, which clips: ink's scratches on a pale part came out #ffffff and ran across a cream creature as hard white lines. A colour with no room left to water turns ink around too: its ground is laid on deeper the more solid the step, and the scratch opens back toward the part's own colour.

One creature is one hand and one pressure, but not one throw of the dice: a texture's noise is the part's own — its place and size on the board — and the hand takes a small swing of its own on each. Without it the head, the ears, the muzzle, the hat and the body all scattered their marks to the same values in the same places, and a creature read as one stamp repeated.

the paper, the GPU material and the boil

Underneath there is exactly one GPU material, a flat MeshBasicMaterial with vertex colours shared by every mesh. No depth test, so renderOrder decides all the stacking, and a layer's fills and ink bake into one geometry — one draw call per layer.

The paper is the board's one shader (src/scene/paper.js): procedural GLSL grain on a single plane, no texture and no tile. It is drawn twice — once behind everything, and once over the whole board after the emoji, so the creatures come out under the paper rather than on it. The pass over the top is not a fade: its alpha follows how far a cell is off the sheet's mean, so flat sheet does nothing and only the speckle lands. The ink keeps its black, which is what lets the creature in front hide the one behind.

three bakes of the same drawing, cycling — this figure never holds still
the boil — nothing is redrawn at runtime: the same drawing is baked 3 times, differing only in jitter phase, and visible cycles once every 1.5–1.9 s. It belongs to the medium, not to motion — the lines shiver even in the bind pose.

the colors

Only the ones that hold up on paper; push the saturation and the hand-drawn feel breaks immediately. And they go in as linear: three.js reads vertex colors as linear space, so an sRGB hex fed raw brightens — the ink #2b2724 would come out a mid grey. Every hex passes through hexToRgb → srgbToLinear in src/color.js, nothing bypasses it.

paper + inks — the ground and four near-black browns. The ink is never pure black; on paper, black is a color too.
fills — skin and cloth. Dusty, close in value, so thirty-odd of them can stand side by side without shouting.
pops — the only saturated colors. One board allows only one or two carriers (makeGrid caps them); scattered everywhere they would stop popping.
darks — imp heads and bodies. Not one ink black but nine: blue-black, brown-black, purple-black — all reading as "black" on paper, all different side by side.
furs — black fur is only moderately black, so a black cat stays a drawing instead of a blob of ink.
accents — greyed in-betweens for hats, patches and small props.
the fixed few — the only colours the roll does not pick: a blush and a tongue are pink, a tooth and an eye's white are white, a mark on a part too dark for the palette's ink is light, the palest muzzle, a heart eye. The thing they paint is always itself, so they always are.

THE GOOFY FUR

Hair is neither a contour nor a filling — in 3D it is a groom, here the goofy fur: ink.fur(path, "SCRIBBLE", { color, passes, spread }), named from GOOFY_FUR. A part hands over the path and the colour; passes, width and spread ride as a style's volume and the rest is the fur's own. One fur stands in the table, SCRIBBLE, and every hairstyle is grown with it:

SCRIBBLE — the scribble

The same path drawn over and over, each pass pushed further out from the root so the mass fans away from the face, every point waving on its own and a share of each push leaning sideways.

14 passes, reaching outward
s.fur — SCRIBBLE — hair is never a filled shape. The same path is drawn over and over with drift and wave, narrower toward the face, reaching outward.

THE GOOFY TYPE

One screen writes words: the name screen's card — the name, the kind, the moves, the rarity. A name comes in any script, so the card does not draw a word glyph by glyph: it traces a real font (src/medium/type.js). A line is written onto a canvas in Noto Sans and Noto Sans KR, served from fonts/ and loaded whatever is typed; the canvas's coverage is traced at half into rings (marching squares), each ring simplified, and a ring inside an odd number of others is a hole. Then the rings go down in the pencil — every point pushed about by a wiggle of its own, the shape filled with its holes, each ring edged in a pencil line — and each boil frame wiggles them its own way, so the words boil with the drawing. A kanji, a hanja or an emoji the two fonts do not hold is written in the platform's font and traced all the same: an emoji comes out an ink silhouette.

the traced type — Hangul, Latin and kana in one line, traced and written by typeWith, the same function the card writes with.

THE GOOFY LETTERS

The other way to draw a word, and the first: the goofy letters (src/medium/letters.js) — 26 capitals, ten digits and a few marks, as strokes, not a font. Every glyph is a list of strokes measured in cap heights — x across its width, y from the baseline to the cap — and each stroke goes down as one line of the same pencil, so a word wanders, sheds and boils like the drawing it sits on. The ♥ and the ★ are filled under their line. They are Latin capitals only, which is why the card writes in the type above; they are kept.

the capitals — letterWith, one pencil line a stroke, in the letters' own hand (half the board's wobble, so a word a few ink widths tall still reads). Every glyph here is read off GLYPHS.
the digits and the marks — the ♥ and a number, × and a count, ★ for stars, · between two words, and what a name is likely to hold.

a corner is two strokes

The pencil re-samples a line at a fixed step (PENCIL.step, 0.01) and walks round a corner by cutting it. On a head that is nothing; on a letter a few steps tall — a small word on a card — a cut corner is a round A. So a corner is two strokes meeting, never one bent line, and where the pen runs past a stroke's end it runs past the way a hand does. A curve is one stroke; a loop — O, 0, 8, a dot — is closed.

one bent line — the apex walked offtwo strokes — the apex kept
the same A, twice — at the height a card's small words stand, magnified. A stroke that short is a stub to the pencil: it keeps its ends and sheds nothing, which is the rest of what keeps the small words legible.

the pens

A letter's pen is a share of its cap height — the letters' own ladder, the way every outline kind names its own, so a word's line grows with the word and a weight that is not on the ladder is not drawn. A title takes L and everything else M.

S · M · L — the same word at each weight, read off LETTER_PENS.

Capitals only. A character the letters have no glyph for — Hangul, kana, an emoji, a lower-case or an accented letter — they cannot write at all. A Korean pencil hand, built from jamo rather than 11,172 syllables, would have been a piece of work of its own; tracing a font (the goofy type, above) writes every script there is.

THE SHAPES

A head is not a circle but a lumpy mass. One recipe, blobPath, makes every closed shape on the board: a superellipse bent by noise, squared and tapered by two knobs.

amount 0 — a circle0.080.2
lumps — noise pushes the radius in and out. At amount 0 it is a perfect ellipse, which is exactly why amount 0 never appears on the board.
square 00.91.8
square — the superellipse exponent. 0 is an ellipse, around 1.5 a rounded box. Box heads and tube bodies live at the right end.
taper −0.350+0.35
taper — the top/bottom width ratio. Positive is wider at the bottom (a pear), negative wider at the top. Most heads sit a little off zero.
smilefrowna shut lid
arcPath — mouths, brows and lids are sections of an ellipse, handed over as a line like any other path, so they wobble like everything else.