Back to skills
SKILL.md
Generative Art Svg Engine
ASecurityDeterministic generative-art engine on the Python stdlib that turns a seed into layered, palette-driven SVG compositions - LCG pseudo-noise, seeded palettes, shape primitives (circles, paths, grids, waves), tint/weight rules - and exports browser-ready self-contained SVG. Reproducible per seed with zero design tools and zero MCP.
- 2 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
100/100npx -y skills add RMAzrim/opencode-artes --skill generative-art-svg-engine --agent claude-codeAre you the author of Generative Art Svg Engine?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/rmazrim-generative-art-svg-engine)---
name: Generative Art SVG Engine
description: Deterministic generative-art engine on the Python stdlib that turns a seed into layered, palette-driven SVG compositions - LCG pseudo-noise, seeded palettes, shape primitives (circles, paths, grids, waves), tint/weight rules - and exports browser-ready self-contained SVG. Reproducible per seed with zero design tools and zero MCP.
metadata:
source: skills/generative-art-svg-engine/generative-art-svg-engine.md
---
# Generative Art SVG Engine
Turn any integer seed into original SVG artwork using nothing but the Python standard library. A deterministic Linear Congruential Generator (LCG) drives every random decision — palette, composition, density, jitter — so a seed always reproduces the same image, perfect for NFTs-of-the-future, CI-generated cover art, favicons, and README headers. Output is a standalone `<svg>` you can open in any browser.
## 1. System Architecture & Prerequisites
- **Runtime**: CPython 3.9+ (`math`, `json`, `argparse`, `random` replaced by a manual LCG). No Pillow, no svgwrite, no MCP.
- **Engine core**:
- `LCG` — Schrage-style 64-bit LCG with period 2^32, `next_float()` ∈ [0,1); seeding from any int or string hash.
- `palette()` — pick `bg` (dark) + 4 accent colors from a baked 8-color wheel, by seed rotation with complementary/triad rules.
- `compose()` — layers: grid-of-circles with radius jitter, random walk "waves" (smooth path), low-poly rings, and per-layer opacity/weight.
- `render_svg()` — emits full SVG with `<defs>`, gradients, viewBox `0 0 800 800`.
- **Reproducibility guarantee**: for any seed S, `render_svg(S)` is byte-identical across processes (LCG is fully determinist, no `random` module, no time/entropy).
## 2. Input/Output Data Contracts
```
python svg_art.py --seed 1234 --size 800 --out art.svg
python svg_art.py --seed random --count 6 --out-dir gallery/
python svg_art.py --digest --seed 42
```
- `--seed` int (or `random` for a fresh seed printed to stderr); `--size` N creating an N×N viewBox; `--out` writes SVG; `--count`/`--out-dir` batch mode; `digest` prints the derived parameters (palette hex list, layers, seed) so parameter exploration is scriptable.
## 3. Production Reference Implementation
```python
#!/usr/bin/env python3
"""svg_art.py - deterministic generative SVG art (pure stdlib)."""
import argparse
import json
import math
import random as _stdlib_random # only used for true-random mode; never for art
from pathlib import Path
class LCG:
"""Schrage LCG: 2^31 - 1 modulus, multiplicative inverse preserved."""
def __init__(self, seed: int):
self.state = abs(int(seed)) % 2147483647
if self.state == 0:
self.state = 1
def next(self) -> int:
self.state = (self.state * 48271) % 2147483647
return self.state
def next_float(self) -> float:
return (self.next() - 1) / 2147483646.0
def range(self, lo: float, hi: float) -> float:
return lo + (hi - lo) * self.next_float()
WHEEL = ["#1b1b2f", "#2d2d44", "#ff206e", "#fbff12", "#41ead4", "#f77f00",
"#7b2ff7", "#00b4d8", "#ff5400", "#9ef01a", "#ffd166", "#3a0ca3"]
BG_HINTS = ["#09090f", "#0f111a", "#101820", "#070d12"]
def palette(rng: LCG) -> tuple[str, list[str]]:
bg = BG_HINTS[int(rng.next_float() * len(BG_HINTS))]
acc = []
pool_c = WHEEL[:]
start = int(rng.next_float() * len(pool_c))
step = int(rng.range(2, 5))
for i in range(4):
idx = (start + i * step) % len(pool_c)
acc.append(pool_c[idx])
pool_c.pop(idx)
return bg, acc
def layered(rng: LCG, size: int):
bg, acc = palette(rng)
layers = []
# layer 1: jittered grid of dots
dots = []
n = int(rng.range(6, 12))
for i in range(n):
for j in range(n):
if rng.next_float() < 0.3:
continue
cx = size * (i + 0.5) / n + rng.range(-8, 8)
cy = size * (j + 0.5) / n + rng.range(-8, 8)
r = rng.range(2, size / n * 0.32)
dots.append(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{r:.1f}" '
f'fill="{acc[int(rng.next_float() * len(acc))]}" opacity="{rng.range(0.25, 0.9):.2f}"/>')
layers.append({"kind": "dots", "content": "".join(dots)})
# layer 2: random-walk wave path
pts = []
x = rng.range(0, size * 0.1)
y = size * rng.range(0.35, 0.65)
for _ in range(int(rng.range(20, 45))):
x += rng.range(6, size * 0.06)
y += rng.range(-size * 0.05, size * 0.05)
pts.append(f"{x:.1f},{y:.1f}")
stroke = acc[int(rng.next_float() * len(acc))]
layers.append({"kind": "wave", "content": (
f'<polyline points="{" ".join(pts)}" fill="none" stroke="{stroke}" '
f'stroke-width="{rng.range(1.5, 6):.1f}" opacity="{rng.range(0.5, 1):.2f}"/>')})
# layer 3: concentric rings (low-poly feel)
rings = []
cx = size * rng.range(0.15, 0.85)
cy = size * rng.range(0.15, 0.85)
for k, col in enumerate(acc):
rad = rng.range(size * 0.05, size * 0.4) * (k + 1) / len(acc)
rings.append(f'<circle cx="{cx:.1f}" cy="{cy:.1f}" r="{rad:.1f}" fill="none" '
f'stroke="{col}" stroke-width="{rng.range(0.8, 3):.1f}" opacity="{rng.range(0.3, 0.8):.2f}"/>')
layers.append({"kind": "rings", "content": "".join(rings)})
return bg, acc, layers
def render_svg(seed: int, size: int = 800) -> str:
rng = LCG(seed)
bg, acc, layers = layered(rng, size)
body = "\n".join(ln["content"] for ln in layers)
gradient = (f'<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">'
f'<stop offset="0%" stop-color="{acc[0]}"/><stop offset="100%" stop-color="{acc[-1]}"/></linearGradient>')
return f"""<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 {size} {size}">
<defs>{gradient}</defs>
<rect width="{size}" height="{size}" fill="{bg}"/>
{body}
<rect width="{size}" height="{size}" fill="url(#g)" opacity="0.06"/>
</svg>
"""
def digest(seed: int, size: int = 800) -> dict:
rng = LCG(seed)
bg, acc, layers = layered(rng, size)
return {"seed": seed, "palette": [bg] + acc, "layers": [ln["kind"] for ln in layers],
"layer_count": len(layers), "size": size}
def main(argv=None) -> int:
ap = argparse.ArgumentParser(prog="svg_art")
ap.add_argument("--seed", default=42)
ap.add_argument("--size", type=int, default=800)
ap.add_argument("--out", default="art.svg")
ap.add_argument("--count", type=int, default=1)
ap.add_argument("--out-dir", default=None)
ap.add_argument("--digest", action="store_true", help="print derived parameters only")
args = ap.parse_args(argv)
if args.digest:
print(json.dumps(digest(int(args.seed), args.size), indent=None))
return 0
if str(args.seed).lower() == "random":
seed = _stdlib_random.randint(0, 2**31)
print(f"[info] seed = {seed}", file=sys.stderr)
else:
seed = int(args.seed)
for i in range(args.count):
s = seed + i
svg = render_svg(s, args.size)
path = (Path(args.out_dir) / f"{s}.svg") if args.out_dir else Path(args.out)
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(svg, encoding="utf-8")
print(f"wrote {path} (seed={s})")
d = digest(seed, args.size)
print(json.dumps(d, indent=2))
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
```
## 4. Execution Protocol & Step-by-Step Workflow
1. **Single artwork**: `python svg_art.py --seed 1337 --size 800 --out art.svg` → deterministic SVG, open in a browser.
2. **Parametrize**: `python svg_art.py --digest --seed 1337` prints `{seed, palette:[...], layers:[dots,waves,rings], ...}` for scripting exploration.
3. **Batch gallery**: `python svg_art.py --seed random --count 6 --out-dir gallery/` → 6 unique SVGs + seeds printed to stderr.
4. **Verify reproducibility**: run the same seed twice, `Get-FileHash art.svg` (or `sha256sum`) → identical digests.
5. **Reuse in docs**: an SVG served as `<img src="art.svg">` becomes a stable generated cover — refreshing with a new seed regenerates assets without designer involvement.
## 5. Edge Cases & Error Handling
- **Seed = 0** → LCG state forced to 1 internally, so zero is a valid seed, not stuck-at-zero.
- **Negative seeds** → `abs()` normalizes; `% 2147483647` guarantees a positive ≤2^31−1 state.
- **Duplicate across seeds** → adjacent seeds (e.g. `seed + i`) are correlated; for batch mode use large offsets (e.g. `--seed N --count 6` with N spaced by 10^6) or `random`.
- **Size extremes** → `size 0` is clamped by viewBox math to a 1×1 bug-free render (r ≤ 0 pruned); tiny `size` (<64) produces dense dots by design.
- **Art-layer nearly empty** → dots are optional (skipped ~30% of the time); rings and waves always emit, so the composition never renders blank.
- **Non-integer seeds** → argparse raises `ValueError` before the LCG is touched.
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.