Turns an SVG logo into a system of brand patterns, and writes a report for every one explaining where it came from and where it belongs.
The system reads a mark, works out what makes it recognisable — its colours, its angles, its symmetry, the unit it is drawn on, the forms that carry its identity — and then builds patterns from those findings rather than from generic decoration. Every pattern is measured before it is offered, and every pattern that is offered comes with a written case for itself.
samples/prism.svg → 25 pattern ideas × 3 settings each → 75 candidates
→ measured, scored, filtered for range
→ 24 patterns, each with a report and five export formats
For each pattern:
| File | What it is |
|---|---|
tile.svg |
The tile as vector artwork, seamless on all four edges |
tile-repeated.svg |
A three by three block, for checking the repeat by eye |
tile.png, repeat.png, small.png |
Raster proofs, including one shrunk to test survival at size |
pattern.css |
A ready background-image rule |
Pattern.jsx |
A React component that draws the same geometry |
loop.lottie.json |
A drifting loop that ends exactly where it began |
report.md |
Why this pattern suits this brand, what was measured, where to use it and where not to |
pattern.json |
Everything above as data, including the settings that produced it |
npm install
npm run build
# Generate a set from a mark
npm run gen -- samples/prism.svg --out ./out --count 24
# Open the studio
npm run studionpm run gen -- --help lists every option: --count, --families,
--variants, --diversity, --seed, --preview, --quiet.
A workspace built on Toolcraft: drop in a mark, read what the engine found in it, generate a set, and work on any one pattern on an infinite canvas — adjusting its density and colour mood, rerolling it for a new variation, watching the drift loop play, and exporting a still or a video.
npm run studio # syncs the engine into the app, then starts it
npm run studio:test # the app's own suiteThe studio is a standalone Toolcraft app in studio/, so it carries its own
copy of the engine under studio/src/engine. That copy is generated —
npm run sync refreshes it from packages/engine/src, and the two never drift
into separate implementations.
vercel.json points a root-directory Vercel project at the studio rather than
at the repository root, which has no site to serve:
| Setting | Value |
|---|---|
| Install | npm install && npm --prefix studio ci |
| Build | npm run studio:build |
| Output | studio/dist |
Two details are worth knowing. studio/ is deliberately not an npm workspace,
because a Toolcraft app has to stand alone, so its dependencies need their own
install step rather than coming along with the root one. And the build command
runs npm run sync first, so the deployed app is always built against the
current packages/engine/src rather than against whatever copy happened to be
committed.
Twenty five ideas. Each one is offered only when it suits the mark: a rosette needs rotational symmetry, a counter grid needs counters, a weave needs a clear angle grid, and the engine ranks them per logo before it spends any time drawing.
| Idea | |
|---|---|
| Motif lattice | The mark's signature form repeated on a measured grid, at the spacing the logo itself is built on. |
| Scatter field | Brand forms spread so no two crowd each other, at mixed sizes, with the spacing checked across the tile edge. |
| Angle weave | Bands running on the logo's own angles, crossing over and under each other at the logo's own line weight. |
| Truchet tiles | A square cell holding one of two mirrored pieces. Laid out at random the pieces join into long continuous runs. |
| Modular grid | A small vocabulary of brand primitives, one per cell, drawn with the logo's corner rounding. |
| Contour echo | The mark repeated outward in even steps, like contour lines, so the silhouette stays readable while the surface fills. |
| Fragment mosaic | The tile cut by lines running on the logo's own angles, with each piece taking a different step of the brand ramp. |
| Radial rosette | The motif turned around a centre as many times as the mark's own rotational symmetry allows. |
| Stripe system | Stripes whose widths are whole multiples of the unit the logo is drawn on, so the rhythm is the mark's own. |
| Counter grid | The space the mark leaves empty, used as the repeating element instead of the mark itself. |
| Idea | |
|---|---|
| Mesh bloom | Soft pools of brand colour bleeding into one another, with every pool that touches an edge drawn again on the opposite side. |
| Aurora ribbons | Wide bands of colour flowing across the tile, shaped by a wave that completes a whole number of cycles so it meets itself at the edge. |
| Wave bands | Layers of brand colour stacked like a landscape, each edge drawn by waves that complete whole cycles across the tile. |
| Halftone field | A gradient made from brand shapes rather than from ink density, with each shape sized by where it sits in the field. |
| Ramp stripes | Bars on the logo's own angle, each carrying a blend across its width, stepping through the brand ramp as they go. |
| Radial bloom | Rings of brand colour spreading out from centres set on a grid, fading before they meet so the surface stays open. |
| Idea | |
|---|---|
| Pixelated motif | The mark redrawn on a coarse grid of squares, which is the form it takes when a brand has to survive very small or very cheap reproduction. |
| Dither ramp | A blend made from two flat brand colours alone, using the ordered dither that screens and presses have always used to fake a tone they cannot print. |
| Pixel mosaic | Blocks of brand colour at two sizes at once, one grid nested inside the other, so the surface has texture without any single element standing out. |
| Pixel glyphs | A small alphabet of square marks invented from the logo's own pixel signature, laid out like characters of a made up script. |
| Idea | |
|---|---|
| Extruded motif | The mark given thickness and lit from one side, so the pattern reads as a surface of solid objects rather than as printed shapes. |
| Cube field | A field of blocks seen from a fixed corner, each rising to a height set by a repeating wave, with three brand tones standing in for three faces of light. |
| Folded ribbon | A surface pleated back and forth, each facet carrying its own blend from the fold that catches the light to the one that falls into shade. |
| Orb grid | Spheres of brand colour on a measured grid, each lit from the same direction, so the surface reads as rows of real objects. |
| Facet field | A surface broken into triangles, each shaded as though it were tilted towards or away from the light, giving a cut crystal look from flat colour alone. |
The engine picks which of its thirty six constructions suit a mark, and where each one's controls should start. It used to do that with thirty six hand written lines — one per construction, of the form start at 0.44, add a bit for rounded corners. Every one of those numbers was guessed, and measured against what the engine actually scores they rank the catalogue at 0.077, which is a coin toss.
They are now a trained model, and it lives in the engine:
packages/engine/src/mind/
features.ts a mark as eighty two numbers
net.ts a small network, written out longhand, no dependencies
marks.ts synthetic logos spanning the space real ones sit in
corpus.ts draw every construction on every mark, judge with the engine's own metrics
train.ts a counted table, plus a network that corrects it from the mark
weights.ts 32 KB of weights, checked in
designer.ts the only part the product calls
Nothing about it is a service. No API key, no request, no download, no
provider, no account. The weights are a constant in the bundle and running the
model is a few thousand multiplications, so it answers before the next frame and
works offline. A test walks the engine source and fails on any fetch, any
environment read, any URL. If the weights are ever missing or stale it falls
back to the old hand written affinities and says so, in the interface and on the
command line.
It teaches itself. The engine could already measure a pattern — seam, ink, brand fit, legibility at size — because it needed to in order to choose twenty four candidates out of seventy five. So the training data is made by drawing 130,896 patterns across 404 marks and writing down what the engine thought of each. Nobody labels anything.
npm run mind:train -w @pattern-gen/engine # rebuild the weights (about 45 minutes)
npm run mind:bench -w @pattern-gen/engine # run the product both ways and compareHeld out across four independent splits, against a baseline that ignores the mark entirely and just ranks by what each construction is worth in general:
| model | mark blind | hand written | |
|---|---|---|---|
| rank agreement | 0.817 | 0.801 | 0.077 |
| top six recall | 0.614 | 0.568 | 0.241 |
Most of the win is measuring rather than guessing. Reading the individual mark adds a small amount on top — sixteen splits out of sixteen positive, so it is real, but it is not the headline.
The headline is what that buys in the product:
All 36 constructions tried, which is what it did before
mean of the 12 returned model 82.48 affinity 82.71 4831ms
Only the first 8 the ranking offers
mean of the 12 returned model 86.10 affinity 81.25 661ms
Eight well chosen constructions beat all thirty six, in a seventh of the time. A ranking is worth almost nothing when you try everything anyway — that first line is noise, and it read the other way round before four constructions were fixed — and it is worth a great deal when it means you no longer have to.
There is a second, separate thing in this repository that uses a large language
model: packages/forge, which writes new constructions in TypeScript. That is a
development tool, it needs your own API key, and the product cannot reach it —
forge depends on engine and the dependency does not run the other way.
packages/engine/ The whole system. No dependencies, runs in Node and the browser.
packages/cli/ The command line runner.
studio/ The Toolcraft workspace. Standalone; carries a generated copy of the engine.
samples/ Four marks with deliberately different properties, for testing.
scripts/ sync-engine.mjs, which copies the engine into the studio.
npm test # the engine: 127 tests, including every generator on every sample
npm run studio:testThe engine suite runs on Node's own test runner with no test framework installed, matching the engine's own zero dependency rule. It checks colour round trips, path and transform parsing, mark reading, every generator on every sample mark for seam freedom, the seam checker itself against deliberately broken tiles, repeatability, and the reports.
DESIGN.md covers the algorithms: how a mark is read, how a
pattern is guaranteed to tile, how patterns are measured and chosen, and how
the reports are written.