JSPM

@zakkster/lite-gen

1.0.3
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 18
  • Score
    100M100P100Q87379F
  • License MIT

Generative art toolkit — simplex noise, flow fields, procedural shapes, OKLCH gradients, deterministic output.

Package Exports

  • @zakkster/lite-gen

Readme

@zakkster/lite-gen

npm version npm bundle size npm downloads npm total downloads TypeScript License: MIT

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-gen

Quick 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 60fps

Recipes

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 code

API

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