Designing Generative SVG Patterns
Generate a single-file SVG pattern driven by parameters (seed, scale, stroke width, color) using simple math (trig, noise, grids) rather than hand-placed shapes:
JavaScriptfunction generatePattern({ size = 400, cols = 8, rows = 8, seed = 1, stroke = '#111', bg = '#fff' }) { let rng = mulberry32(seed); const cell = size / cols; let shapes = ''; for (let y = 0; y < rows; y++) { for (let x = 0; x < cols; x++) { const cx = x * cell + cell / 2; const cy = y * cell + cell / 2; const r = cell * 0.3 * (0.5 + rng()); shapes += `<circle cx="${cx.toFixed(2)}" cy="${cy.toFixed(2)}" r="${r.toFixed(2)}" fill="none" stroke="${stroke}" stroke-width="1"/>`; } } return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${size} ${size}"> <rect width="${size}" height="${size}" fill="${bg}"/> ${shapes} </svg>`; } function mulberry32(a) { return function() { a |= 0; a = (a + 0x6D2B79F5) | 0; let t = Math.imul(a ^ (a >>> 15), 1 | a); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; }
Progress:
- Step 1: Pick a category (grid, radial, noise, flow, isometric, organic, distortion, physics) — this determines the coordinate system and base algorithm.
- Step 2: Choose a generative primitive (grid iteration, polar/radial loop, Perlin/simplex noise field, particle/flow simulation, isometric projection, L-system/organic growth).
- Step 3: Parametrize everything that could vary: seed, density, scale, rotation, stroke width, color palette, animation toggle.
- Step 4: Render using only basic SVG primitives (line, circle, path, polygon) — avoid raster/filters unless the category is "noise" or "distortion" and truly needs
feTurbulence. - Step 5: Keep the viewBox square (e.g.,
0 0 400 400) so patterns tile/preview consistently as thumbnails. - Step 6: Verify visual balance — no clipped shapes at edges, consistent stroke weight, no overlapping labels/artifacts.
- Step 7: Export/minify — strip unnecessary precision (2 decimal places), remove default attributes, inline as
<svg>or save as.svgfile matching kebab-case pattern name. - Step 8: Name the pattern (Title Case, 2-3 words, descriptive of visual result, e.g., "Flow Dots", "Iso Cube Wireframe") and tag with its category.
Example 1: Input: Category = "flow", concept = dots drifting along a vector field Output: A grid of seed points, each offset along the gradient of a noise function computed at its position, rendered as small filled circles with size/opacity mapped to field magnitude. Named "Flow Dots".
Example 2: Input: Category = "isometric", concept = stacked cubes scattered with jitter Output: A grid of isometric cube wireframes (three rhombus faces per cube via 30°/150° projection), each cube's position jittered by a seeded random offset and z-height varied for a scattered stacking effect. Named "Scattered Cube Grid".
Example 3:
Input: Category = "radial", concept = concentric rings broken into arcs
Output: Concentric circles subdivided into arc segments (via <path> with arc commands), some segments randomly omitted per ring to create a "broken ring" look, radius spacing following a geometric progression. Named "Broken Ring".
- Determinism first: always drive randomness from a seeded PRNG (never
Math.random()directly) so patterns are reproducible and swappable via aseedparam. - Few primitives, many repetitions: the "minimal" aesthetic comes from repeating one simple shape/rule across a grid or field, not from complex individual shapes.
- Stroke-only or fill-only, rarely both: pick one visual mode per pattern for consistency; mixing stroke+fill muddies the minimal look.
- Square, self-contained viewBox: no external assets, fonts, or filters unless essential (e.g.,
feTurbulencefor true "noise" category patterns). - Naming convention: Title Case for display name, snake_case or kebab-case for the file/route slug — keep both in sync.
- Category tagging: every pattern belongs to exactly one primary category matching its underlying generative technique, not just its visual appearance.
- Thumbnail-safe: design so the pattern reads clearly at small (aspect-square) preview sizes — avoid overly fine detail that disappears when scaled down.
- Don't hand-place individual shapes with magic-number coordinates — always derive positions from a formula/loop so the pattern scales to any grid size.
- Don't use unseeded randomness — it breaks reproducibility and prevents "liked/saved" variants from being recreated.
- Don't rely on raster filters or embedded bitmaps — defeats the purpose of a scalable, lightweight SVG library.
- Don't mix multiple unrelated generative techniques in one pattern (e.g., isometric cubes + radial noise) — keep each pattern's concept singular and legible.
- Don't ignore edge behavior — shapes clipped awkwardly at the viewBox boundary look unpolished; either pad the grid or intentionally bleed to the edge.