@zakkster/lite-gen
v1.0.2
Published
Generative art toolkit — simplex noise, flow fields, procedural shapes, OKLCH gradients, deterministic output.
Maintainers
Readme
@zakkster/lite-gen
A generative art toolkit. Deterministic, color-correct, small.
The only generative art toolkit using OKLCH with zero allocations and deterministic output.
🎬 Live Demo (GenEngine)
https://codepen.io/Zahari-Shinikchiev/full/yyaMXov
Why This Library?
| Feature | Lite-Gen | p5.js | Processing.js | regl | three.js | |---|---|---|---|---|---| | Deterministic | Yes | No | No | Yes | No | | Zero-GC Hot Path | Yes | No | No | Yes | No | | OKLCH Color | Yes | No | No | No | No | | Simplex Noise | Yes | Via addon | Yes | No | No | | Canvas2D Optimized | Yes | Medium | Medium | No | No | | Bundle Size | Tiny | Large | Large | Medium | Large |
Creative coding is exploding. People want small libs, deterministic output, color-correct gradients, and easy APIs. Lite-Gen delivers all four.
Performance
Noise Generation (1,000,000 samples)
| Library | Speed | Allocations | Deterministic | |---|---|---|---| | Lite-Gen | Fastest | 0 | Yes | | simplex-noise | Medium | Medium | Yes | | perlin-noise | Slow | High | Yes |
Particle Art (10,000 particles, 60 FPS)
| Engine | Allocs/Frame | Frame Time (ms) | GC (10s) | Deterministic | |---|---|---|---|---| | Lite-Gen | 0 | 1.4 | 0 | Yes | | p5.js | High | 8–12 | 4–6 | No | | vanilla OOP | Very High | 12–20 | 10+ | No |
Installation
npm install @zakkster/lite-genQuick Start — Static Art
import { GenEngine, Pattern, Shape } from '@zakkster/lite-gen';
const gen = new GenEngine(canvas, { width: 800, height: 600, seed: 42 });
gen.draw(({ art, rng, noise, width, height }) => {
art.background({ l: 0.05, c: 0.02, h: 250 });
// Noise-driven dot field
Pattern.noiseDots(art, {
noise, spacing: 15, maxRadius: 6,
colorFn: (n) => ({ l: 0.4 + n * 0.5, c: 0.2, h: 200 + n * 80 }),
});
});
gen.render();
gen.save('noise-dots.png');Quick Start — Animated Art
const gen = new GenEngine(canvas, { seed: 42 });
gen.draw(({ art, noise, time, dt, width, height }) => {
art.clear();
// Animated noise field — time drives the Z axis
for (let x = 0; x < width; x += 10) {
for (let y = 0; y < height; y += 10) {
const n = (noise.noise3D(x * 0.005, y * 0.005, time * 0.5) + 1) / 2;
art.dot(x, y, n * 5, { l: n, c: 0.15, h: 200 });
}
}
});
gen.start(); // runs at 60fpsRecipes
Flow Field Trace Art
import { GenEngine, FlowField, SimplexNoise, Pattern } from '@zakkster/lite-gen';
import Random from '@zakkster/lite-random';
const gen = new GenEngine(canvas, { seed: 123 });
gen.draw(({ art, rng, noise, width, height }) => {
art.background({ l: 0.02, c: 0.01, h: 240 });
const field = new FlowField({ noise, scale: 0.004, strength: 3 });
Pattern.flowTrace(art, {
field, rng, particleCount: 800, steps: 300, stepSize: 1.5,
colorFn: (i, t) => ({ l: 0.5 + t * 0.3, c: 0.2, h: t * 360 }),
lineWidth: 0.4, alpha: 0.15,
});
});
gen.render();Poisson Disk Scatter
gen.draw(({ art, rng, width, height }) => {
art.background({ l: 0.95, c: 0.02, h: 60 });
const points = Shape.poissonDisk(width, height, 20, rng);
for (const p of points) {
const hue = (p.x + p.y) * 0.3;
art.dot(p.x, p.y, 4, { l: 0.6, c: 0.2, h: hue % 360 });
}
});Lissajous Figure
gen.draw(({ art, width, height }) => {
art.background({ l: 0.05, c: 0.01, h: 0 });
const points = Shape.lissajous(width/2, height/2, 3, 4, 300, 200, 500);
art.path(points, { l: 0.8, c: 0.2, h: 280, a: 0.6 }, { lineWidth: 2, close: true });
});Agent-Based Art with lite-particles
Use @zakkster/lite-particles as autonomous "painter" agents that wander via the flow field:
import { Emitter } from '@zakkster/lite-particles';
const emitter = new Emitter({ maxParticles: 200 });
const field = new FlowField({ noise, scale: 0.01, strength: 2 });
// Spawn agents
emitter.emitBurst(100, (i) => ({
x: rng.range(0, width), y: rng.range(0, height),
vx: 0, vy: 0, life: 999, maxLife: 999,
data: { trail: [] },
}));
// Each frame
emitter.update(dt);
emitter.draw(ctx, (ctx, p, life) => {
field.applyTo(p, dt);
p.data.trail.push({ x: p.x, y: p.y });
// Draw ribbon trail
if (p.data.trail.length > 2) {
ctx.strokeStyle = toCssOklch({ l: 0.6, c: 0.15, h: p.x * 0.5 });
ctx.beginPath();
ctx.moveTo(p.data.trail[0].x, p.data.trail[0].y);
for (const pt of p.data.trail) ctx.lineTo(pt.x, pt.y);
ctx.stroke();
}
});Reseed and Regenerate
gen.seed(999); // new seed = new art
gen.clear();
gen.render(); // completely different output, same codeAPI
GenEngine
| Method | Description |
|---|---|
| new GenEngine(canvas, options?) | Create with optional width, height, seed, animate |
| .draw(fn) | Register draw callback. Receives { art, ctx, rng, noise, width, height, time, dt } |
| .render() | Execute draw once (static art) |
| .start() / .stop() | Animation loop |
| .seed(n) | Reseed RNG + noise |
| .resize(w, h) | Resize canvas |
| .save(filename?) | Download as PNG |
| .destroy() | Clean teardown |
Other Exports
SimplexNoise, FlowField, Shape, ArtCanvas, Pattern
License
MIT
