メインコンテンツまでスキップ

kumiko-gen spec

  • 作成:
  • 更新:
  • 著者:
    Takeshi Takatsudo

Overview

kumiko-gen generates deterministic kumiko-style SVG thumbnails from article slug strings. Published as npm package @takazudo/kumiko-gen. Zero external dependencies.

Input: slug string. Output: SVG string. Same slug always produces the same SVG.

Generation pipeline

  1. FNV-1a hash: slug string → 32-bit unsigned integer
  2. Mulberry32 PRNG seeded with hash
  3. Seeded random determines: layer count, pattern selection, per-layer color, overlap count, stroke widths, grid divisions, layer transforms
  4. Generate equilateral triangle grid
  5. Render each layer with per-layer color, rotation, position offset, and per-overlap stroke width
  6. Assemble SVG with viewBox (zoom support)

All randomness comes from the seeded PRNG. No Math.random().

Options

interface KumikoOptions {
size?: number; // canvas size in px (default: 800)
divisions?: number; // grid columns (default: seeded from [6, 8, 10])
zoom?: number; // viewBox crop factor (default: 1)
fg?: string; // override stroke color for all layers
bg?: string; // background color (default: "#2d2d2d")
strokeWidth?: number; // override stroke width (default: seeded per overlap)
colorScheme?: string; // color scheme name or "random" for deterministic per-slug selection
}

Color scheme

36 built-in color schemes (Dracula, Nord, Catppuccin, Gruvbox, TokyoNight, etc.) are available. Each scheme has 1 background + 7 foreground colors.

The default scheme uses a dark background with 8 foreground colors:

IndexHexName
bg#2d2d2ddark background
0#4a4a4adark gray
1#b5524abrick red
2#5ea85egreen
3#c8a64egold
4#737d8eslate blue
5#a87a96mauve
6#5a8a8eteal
7#d5d5d5light gray

--color-scheme <name> selects a specific scheme. --color-scheme random picks a deterministic scheme per slug (derived from the hash, not the PRNG). --list-color-schemes prints all available names.

--fg overrides all layers to a single color. --bg overrides background. These take priority over the selected scheme.

Stroke style

Fixed for all layers:

  • stroke-linecap="square"
  • stroke-linejoin="bevel"

Triangle grid

  • Canvas tiled with equilateral triangles
  • divisions controls column count (6, 8, or 10)
  • Row height: colWidth * (√3 / 2)
  • Each row alternates upward/downward triangles
  • Grid extends slightly beyond canvas to fill edges

Triangle data:

  • vertices: [Point, Point, Point]
  • centroid: Point (average of 3 vertices)
  • isUpward: boolean

Patterns (9 types)

Basic (3 elements/triangle)

NameDescription
asanohaVertex → centroid lines. Star motif when mirrored.
mitsukudeEdge midpoint → centroid lines. Y-shape.
gomaVertex → opposite edge midpoint lines. Inner subdivision.
shippoArcs between vertex pairs. Radius = edge length * 0.5.

Complex (6-12 elements/triangle)

NameElementsDescription
yae-asanoha6All vertex + midpoint → centroid radials.
kikko6Inner triangle (40% scale) + outer-to-inner vertex connections.
sakura6Quadratic bezier petals at vertices + centroid-to-midpoint spokes.
bishamon9Inner triangle (35%) + vertex→centroid + inner→opposite midpoint cross.
izutsu12Two concentric triangles (65%, 30%) + radial connections between all 3 levels.

Layers

Multiple distinct patterns overlaid in one SVG. Each layer gets its own color from the palette.

ProbabilityLayer count
40%2
40%3
20%4

Patterns selected by shuffling all 9, taking first N.

Per-layer transform

Each layer gets:

  • Color: random from 8-color palette
  • Rotation: 0–360 degrees around canvas center
  • Position offset: ±20% of canvas size in X and Y

Overlap

Each layer independently rolls for overlap copies of the same pattern.

ProbabilityCopies
50%1 (no overlap)
30%2
20%3

Additional copies add on top of layer transform:

  • Extra offset: ±1.5% of canvas size
  • Extra rotation: ±4 degrees

All overlaps within the same layer share the same color.

Stroke width

Each overlap copy gets its own random stroke width. Options are scaled by total density (sum of all overlap copies across all layers):

DensityStroke width options
11, 1.5, 2, 3, 4
20.5, 1, 1.5, 2, 3
30.5, 1, 1.5, 2
40.5, 0.75, 1, 1.5
5+0.5, 0.75, 1

--stroke-width CLI flag overrides all random widths with a single value.

Zoom

Crops SVG viewBox to center portion.

viewSize = size / zoom
viewOffset = (size - viewSize) / 2

Full geometry is still generated; only the visible area changes.

CLI

pnpm kumiko-gen <slug> [options]
FlagDescription
--size <n>Canvas size (default: 800)
--zoom <n>Zoom factor (default: 1)
--color-scheme <name>Color scheme name or random
--list-color-schemesPrint available color scheme names and exit
--fg <color>Override stroke color for all layers
--bg <color>Background color
--stroke-width <n>Override stroke width
--divisions <n>Grid column count
--out <path>Output file path
--out-dir <dir>Output directory ({dir}/{slug}.svg)

Default output (standalone): <slug>.svg in current directory.
In zpaper, the root script passes --out-dir blog/public/thumbnails --color-scheme random --zoom 5 so pnpm kumiko-gen <slug> outputs a zoomed, uniquely colored SVG to blog/public/thumbnails/<slug>.svg.

Source

Source code: github.com/Takazudo/kumiko-gen