Skip to content

Repository files navigation

Pattern Gen

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

What it produces

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

Quick start

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 studio

npm run gen -- --help lists every option: --count, --families, --variants, --diversity, --seed, --preview, --quiet.

The studio

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 suite

The 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.

Deploying it

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.

The four families

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.

Shape (10)

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.

Gradient (6)

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.

Pixel (4)

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.

Dimensional (5)

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 model in the engine

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 compare

What it changes

Held 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.

Layout

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.

Tests

npm test          # the engine: 127 tests, including every generator on every sample
npm run studio:test

The 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.

How it works

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.

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages