Package Exports
- @zakkster/lite-gen
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