CodeChroma All articles
Colour Inspiration

Sett in Stone: Building a Tartan Designer That Obeys the Thread Count

CodeChroma
Sett in Stone: Building a Tartan Designer That Obeys the Thread Count

Photo: Bill Harrison , CC BY-SA 2.0, via Wikimedia Commons

There's a particular pleasure in watching a tartan appear out of a list of numbers. No rectangles nudged around by hand, no eyeballing a grid — just a sequence of thread counts and a mirror, and suddenly you've got cloth. This is a build log for a small in-browser tool that does exactly that, plus the bits that kept tripping me up: twill, joins, and the awkward question of what you're allowed to call the thing when you're finished.

The sett is the whole pattern

Everything starts with the sett: the order and count of threads in one repeat, written out from the pivot. A shorthand you'll see in weaving books looks like B24 K4 G24 — colour letter, thread count. In code, an array of pairs is plenty:

const halfSett = [
  { name: 'moss', hex: '#2f4a34', count: 24 },
  { name: 'ink',  hex: '#12201a', count: 4  },
  { name: 'rust', hex: '#8c3b26', count: 6  }
];

Note the name: halfSett. Real setts are symmetric about a pivot thread, so you author one side and let the machine do the mirroring. That isn't laziness, it's the definition.

A pivot, not a seam

Mirroring is one line of JavaScript, but it's one line people get wrong in the same way every time: they mirror the entire array and end up with a double-width band at the join.

const fullSett = [...halfSett, ...halfSett.slice(0, -1).reverse()];

The slice(0, -1) is doing all the heavy lifting. The last band of the half sett is the pivot — it sits at the centre of the repeat and appears once, not twice. Drop that slice and your pivot band quietly doubles. The cloth still looks like tartan, so nobody spots it until they sit down and count threads.

Simulating 2/2 twill

Tartan isn't flat colour. Every square in the grid is a crossing of a warp thread and a weft thread, and because the weave is twill, the two take turns showing. In a 2/2 twill each thread floats over two neighbours and under two, shifting one thread along with every pick — that's where the diagonal comes from.

You can fake it convincingly with a modulo test. For a cell at (x, y), the warp is on top when (x + y) % 4 < 2. Where the warp shows, paint the warp colour; where the weft shows, paint the weft colour. Since tartan uses the same sett in both directions, every square ends up mixing two colours from the palette, and the intersections read darker than either band alone — exactly as they do in wool.

const warpOnTop = (x + y) % 4 < 2;
const shade = warpOnTop ? warpColour : weftColour;

Add a small lightness nudge — two to four percent in OKLCH — to the weft so the diagonal survives at a glance, and you're most of the way to something that reads as cloth rather than a spreadsheet.

SVG or canvas?

Both, if you're not precious about bundle size. SVG suits a designer tool: wrap the weave in a <pattern> and the browser tiles it for free, so people can zoom in to inspect a single thread without a redraw. Canvas is the better bet when you want to export a PNG at a fixed size, or when a gallery view is rendering a few hundred setts at once. Keep the sett maths in one module and let each renderer consume the same expanded thread list.

The halving-and-doubling check

Traditional weavers will halve or double a sett depending on the weight of cloth they're after. It's a good discipline for a generator too, because it exposes errors that are invisible at full size.

Halving only works when every count in the sett is even. Write the check — and, crucially, report which bands fail rather than simply refusing to proceed:

const oddBands = halfSett.filter(band => band.count % 2 !== 0);

Doubling is always safe arithmetically, but it changes the scale of the pattern. A sett that reads as a neat check on a scarf becomes a bold block on a screen. If you offer a double button, surface the resulting thread count so people can see what they've done to the cloth.

There's a second, less obvious check worth building in. Once the sett is expanded, flag any run of three or more adjacent bands in the same colour — that's usually a pivot handled wrongly somewhere upstream, and it's the kind of thing that survives review because it looks deliberate. Treat it as a warning, not an error; sometimes a designer means it.

Getting from dyed wool to OKLCH

Swatch photos lie. A photo of a dyed hank on a windowsill in Leeds in November carries the whole of that afternoon in it. Sample anyway, then convert to OKLCH and treat lightness and chroma as the things you actually care about, not the hex value you picked up.

Two practical rules. Keep chroma modest — real tartan dyes are rarely as saturated as people expect, and oversaturated bands break the illusion immediately. And enforce a minimum lightness gap between neighbouring bands: anything within roughly 0.05 L of its neighbour will collapse into a single stripe at small sizes. If a sample fails that test, nudge one of the pair rather than accepting it.

--band-rust: oklch(48% 0.11 38);
--band-moss: oklch(38% 0.05 145);

Keep a hex fallback alongside anything that might meet an older browser, and generate both from the same source object so they can't drift apart.

The favicon problem

At 16 pixels, a sixty-thread repeat is noise. The fix is to render a reduced sett at small sizes rather than shrinking the full one — collapse the repeat to somewhere between eight and twelve bands by merging the smallest neighbouring counts, keeping the two or three dominant colours intact. It won't be the same cloth, but it'll be recognisably the same tartan, which is all a favicon has to manage. Generate it from the same sett object so it updates whenever the designer changes.

Named setts, and the awkward bit

Plenty of setts carry names, and a fair few of those names belong to families, regiments or institutions. The Scottish Register of Tartans is the place to check what you're looking at, and some entries carry restrictions on weaving and use. None of that means you can't build a generator — it means you should be careful with labels.

If someone loads a well-known sett into your tool and starts swapping the colours, the honest caption is 'after' the original or 'based on a traditional sett', not the original name itself. Give each pattern a free-text name field and a separate notes field for provenance, and put the caveat in the interface rather than burying it in a footer. It costs nothing, and it's the difference between a tool that respects a living tradition and one that treats it as a stock colour library.

Wrapping up

Strip it back and the whole thing is a list, a mirror and a modulo. The generosity lives in the details: the pivot handled properly, the twill that makes it look like cloth rather than a chart, and the small courtesies around names. Build the core in an afternoon, then spend a fortnight on the twill. That, honestly, is the job.

All Articles

Related Articles

The Virtual Signwriter: Coach-Enamel Colours in p5.js

The Virtual Signwriter: Coach-Enamel Colours in p5.js

Jewel Tones Behind the Lead: A Stained-Glass Palette Generator for the Web

Jewel Tones Behind the Lead: A Stained-Glass Palette Generator for the Web

Bulbs, Bloom and Budget: Coding a Piccadilly Marquee That Glows Without Melting the GPU

Bulbs, Bloom and Budget: Coding a Piccadilly Marquee That Glows Without Melting the GPU