@nexart/codemode-sdk
v1.1.1
Published
NexArt Code Mode SDK - Canonical execution engine for deterministic generative art
Maintainers
Readme
NexArt Code Mode Runtime SDK
Version: 1.0.2 (Protocol v1.0.0)
╔══════════════════════════════════════════════════════════════════════════════╗ ║ @nexart/codemode-sdk — Canonical Code Mode Authority ║ ║ ║ ║ This SDK defines the official Code Mode execution surface. ║ ║ All implementations (NexArt, ByX, external) MUST use this SDK. ║ ║ ║ ║ Protocol: nexart ║ ║ Engine: codemode ║ ║ SDK Version: 1.0.2 ║ ║ Protocol Version: 1.0.0 ║ ║ Phase: 1 ║ ║ Enforcement: HARD ║ ╚══════════════════════════════════════════════════════════════════════════════╝
PROTOCOL LOCK — v1.0.0
| Property | Value | |----------|-------| | Protocol Name | NexArt Code Mode | | Version | v1.0.0 | | Status | HARD LOCKED | | Phase | 1 | | Lock Date | December 2024 |
This protocol surface is frozen. Any breaking change requires v2.0.0.
The following are locked and will not change in v1.x:
- Execution model (Static and Loop modes)
- VAR[0..9] specification (0-10 read-only variables, missing indices return 0)
- Determinism guarantee (seed + VAR → identical output)
- Time semantics (t, frameCount, time, tGlobal)
- Random and noise behavior (seeded Mulberry32, Perlin)
- Forbidden patterns list (13 patterns)
- Canvas pre-initialization (no createCanvas)
A minimal, deterministic rendering engine for generative art.
Protocol Authority
This SDK is the single source of truth for Code Mode semantics.
If someone asks: "How does Code Mode work in NexArt?"
The answer is: "Whatever @nexart/codemode-sdk does — that is the protocol."
What's New in v1.0.2
- VAR input is now optional (0-10 elements)
- Omit
varsor pass[]for empty (defaults to all zeros) - Input length must be 0-10 (throws if > 10)
- Values must be finite numbers in [0, 100] (throws if out of range)
- Runtime VAR is ALWAYS 10 elements (padded with zeros for consistency)
- Omit
- Backwards compatible: existing code passing 10 elements works unchanged
v1.0.1
- Protocol Lock section formalized with HARD LOCKED status
- VAR specification clarified with enforcement tables
v1.0.0 (Protocol Lock)
- Protocol Lock: Phase 1 execution surface is now LOCKED
- Canonical Entry Point:
executeCodeMode()is the official execution API - Protocol Metadata: All executions include protocol headers for verification
- VAR[0..9] Protocol Variables: First-class protocol inputs (read-only, 0-100)
- Full CSS Color Support: hex, rgb(), rgba(), hsl(), hsla()
- Determinism Guarantee: Same code + seed + vars = identical output
What This SDK Is
This SDK provides the canonical runtime for executing p5.js-style generative art:
- Static Mode: Executes
setup()only, outputs PNG - Loop Mode: Frame-authoritative rendering, outputs MP4
- Deterministic: Seed-controlled randomness, no external state
- Protocol-Compliant: All outputs include verification metadata
The SDK enforces the NexArt Code Mode Protocol v1.0.0 for reproducible, mint-safe generative art.
What This SDK Is NOT
- Not a suggestion — This SDK IS the protocol, not a reference implementation
- Not a UI library — No React components, no wallet integration
- Not an IPFS client — Does not handle storage or minting
- Not p5.js — Uses a minimal subset of p5.js-like functions
Installation
# Copy the sdk/codemode folder to your project
cp -r sdk/codemode your-project/lib/codemodeCanonical API
executeCodeMode(input: ExecuteCodeModeInput): Promise<ExecuteCodeModeResult>
This is the official, canonical entry point for Code Mode execution.
All implementations MUST use this function.
import { executeCodeMode } from '@nexart/codemode-sdk';
const result = await executeCodeMode({
source: `
function setup() {
background(255);
fill(0);
let size = map(VAR[0], 0, 100, 50, 200);
ellipse(width/2, height/2, size);
}
`,
width: 1950,
height: 2400,
seed: 12345,
vars: [50, 75, 0, 0, 0, 0, 0, 0, 0, 0],
mode: 'static'
});
// Result includes protocol metadata
console.log(result.metadata.protocol); // 'nexart'
console.log(result.metadata.engine); // 'codemode'
console.log(result.metadata.protocolVersion); // '1.0.0'
console.log(result.metadata.deterministic); // true
console.log(result.image); // PNG BlobInput Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| source | string | ✅ | Code with setup() and optional draw() |
| width | number | ✅ | Canvas width in pixels |
| height | number | ✅ | Canvas height in pixels |
| seed | number | ✅ | Seed for deterministic randomness |
| vars | number[] | ❌ | VAR[0..9] values (0-100), defaults to all zeros |
| mode | 'static' \| 'loop' | ✅ | Execution mode |
| totalFrames | number | ⚠️ | Required for loop mode |
Result Structure
interface ExecuteCodeModeResult {
image?: Blob; // Static mode: PNG
video?: Blob; // Loop mode: MP4
frames?: ImageData[]; // Optional: raw frame data
metadata: {
protocol: 'nexart';
engine: 'codemode';
protocolVersion: '1.0.0';
phase: 1;
deterministic: true;
seed: number;
vars: number[];
width: number;
height: number;
mode: 'static' | 'loop';
totalFrames?: number;
}
}Legacy API
⚠️ Note: The
createEngine()API is still supported but new implementations should useexecuteCodeMode().
createEngine(config: EngineConfig): Engine
Create a rendering engine instance.
import { createEngine } from './codemode';
const engine = createEngine({
mode: 'static', // 'static' | 'loop'
width: 1950, // Optional, default: 1950
height: 2400, // Optional, default: 2400
duration: 2, // Loop mode only, 1-4 seconds
fps: 30, // Loop mode only, default: 30
});engine.run(options: RunOptions): Promise<void>
Execute code and produce output.
await engine.run({
code: `
function setup() {
background(255);
fill(0);
// Use VAR for external control
let size = map(VAR[0], 0, 100, 50, 200);
ellipse(width/2, height/2, size);
}
`,
seed: 12345, // Optional: seed for deterministic randomness
vars: [50, 75, 0, 0, 0, 0, 0, 0, 0, 0], // Optional: VAR[0..9] values (0-100)
onPreview: (canvas) => {
// Optional: called with canvas after first frame
},
onProgress: (info) => {
// Optional: progress updates
console.log(info.message, info.percent + '%');
},
onComplete: (result) => {
// Required: called with final result
console.log(result.type); // 'image' | 'video'
console.log(result.blob); // Blob
},
onError: (error) => {
// Optional: called on error
console.error(error);
},
});Protocol Variables (VAR[0..9]) — Protocol v1.0.0
Protocol variables are first-class inputs that control artwork parameters.
VAR Specification (SDK v1.0.2):
| Property | Value | Enforcement | |----------|-------|-------------| | Input count | 0-10 (VAR[0]..VAR[9]) | HARD — throws if > 10 | | Runtime count | Always 10 | Padded with zeros | | Type | finite number | HARD — throws if non-number | | Range | 0-100 | HARD — throws if out of range | | Mutability | Read-only | HARD — Proxy blocks writes | | Injection | Before execution | Guaranteed | | Lifecycle | Stable for entire render | Guaranteed | | Default | All zeros | If not provided |
// Pass values when running (0-10 elements)
await engine.run({
code: myCode,
vars: [80, 50, 25], // VAR[0]=80, VAR[1]=50, VAR[2]=25, VAR[3..9]=0
onComplete: (result) => { /* ... */ },
});
// Or omit entirely (all zeros)
await engine.run({
code: myCode,
onComplete: (result) => { /* ... */ },
});
// Access in sketch code
function setup() {
let density = map(VAR[0], 0, 100, 10, 200);
let speed = map(VAR[1], 0, 100, 0.5, 5);
// VAR[5] returns 0 if not provided
}Rules (Protocol Law):
- Input accepts 0-10 elements; runtime VAR is always 10 elements
- VAR is injected BEFORE code execution (padded with zeros if needed)
- VAR values are READ-ONLY at runtime (Proxy-protected)
- Writing to VAR throws a protocol error
- Values MUST be in range 0-100 (throws if out of range)
- Same code + same seed + same VARs = identical output
engine.stop(): void
Cancel a running render (Loop mode only).
engine.getConfig(): EngineConfig
Get the resolved engine configuration.
Execution Rules
Static Mode
setup()is executed oncedraw()is NOT executed- Canvas is captured as PNG
- Time variables are all
0
Loop Mode
setup()is executed oncedraw()is executed once per frame- Canvas is cleared before each
draw()call - If artist calls
background()in draw, it paints over the clear - No canvas persistence between frames
Time Variables:
| Variable | Type | Description |
|----------|------|-------------|
| frameCount | int | Current frame (0, 1, 2, ...) |
| t | float | Normalized time [0.0, 1.0) |
| time | float | Elapsed seconds |
| tGlobal | float | Alias for t |
Forbidden Patterns — LOCKED v1.0.0
The following 13 patterns are rejected with [Code Mode Protocol Error]:
| Pattern | Reason |
|---------|--------|
| setTimeout | Async timing breaks determinism |
| setInterval | Async timing breaks determinism |
| requestAnimationFrame | Async timing breaks determinism |
| Date.now() | Time-based entropy forbidden |
| new Date() | Time-based entropy forbidden |
| Math.random() | Use seeded random() instead |
| fetch() | External IO forbidden |
| XMLHttpRequest | External IO forbidden |
| createCanvas() | Canvas is pre-initialized |
| document.* | DOM access forbidden |
| window.* | DOM access forbidden |
| import | External imports forbidden |
| require() | External imports forbidden |
Additionally in Loop Mode:
noLoop()— Incompatible with frame capture
Example: Static Mode
import { createEngine } from './codemode';
const engine = createEngine({ mode: 'static' });
await engine.run({
code: `
function setup() {
background(30);
noStroke();
for (let i = 0; i < 100; i++) {
fill(random(255), random(255), random(255));
ellipse(random(width), random(height), 50);
}
}
`,
onComplete: (result) => {
// result.type === 'image'
// result.blob is a PNG Blob
const url = URL.createObjectURL(result.blob);
document.body.innerHTML = `<img src="${url}" />`;
},
});Example: Loop Mode
import { createEngine } from './codemode';
const engine = createEngine({
mode: 'loop',
duration: 2, // 2 second loop
});
await engine.run({
code: `
function setup() {
// Called once
}
function draw() {
background(30);
// t goes from 0 to 1 over the loop duration
let x = width/2 + cos(t * TWO_PI) * 200;
let y = height/2 + sin(t * TWO_PI) * 200;
fill(255);
ellipse(x, y, 80);
}
`,
onProgress: (info) => {
console.log(info.message);
},
onComplete: (result) => {
// result.type === 'video'
// result.blob is an MP4 Blob
const url = URL.createObjectURL(result.blob);
document.body.innerHTML = `<video src="${url}" autoplay loop />`;
},
});Supported Functions
The SDK includes a minimal p5.js-like runtime with:
Drawing:
background, clear, fill, noFill, stroke, noStroke, strokeWeight
Shapes:
ellipse, circle, rect, square, line, point, triangle, quad, arc
Vertex:
beginShape, vertex, endShape
Transform:
push, pop, translate, rotate, scale, resetMatrix
Color:
colorMode, color, lerpColor, red, green, blue, alpha, hue, saturation, brightness
Color Formats:
All of the following are accepted by fill(), stroke(), background():
- Grayscale:
fill(128),fill(128, 127) - RGB:
fill(255, 0, 0),fill(255, 0, 0, 128) - Hex:
fill('#ff0000'),fill('#f00') - CSS:
fill('rgb(255,0,0)'),fill('rgba(255,0,0,0.5)'),fill('hsl(180,50%,50%)')
Protocol Variables:
VAR — Array of 10 values (VAR[0] through VAR[9])
Math:
random, noise, map, constrain, lerp, dist, mag, norm
Trig:
sin, cos, tan, asin, acos, atan, atan2, radians, degrees
Constants:
PI, TWO_PI, HALF_PI, QUARTER_PI, width, height, frameCount
Video Encoding
Loop Mode requires server-side video encoding. The SDK calls:
POST /api/encode-loopEnsure your server has this endpoint available (NexArt provides this).
Files
sdk/codemode/
├── index.ts # Main export
├── engine.ts # createEngine entry point
├── types.ts # TypeScript types
├── static-engine.ts # Static mode implementation
├── loop-engine.ts # Loop mode implementation
├── p5-runtime.ts # p5.js-like runtime
└── README.md # This fileLicense
MIT License
Copyright (c) 2024 NexArt
External Builders
This SDK is designed for use by:
- NexArt App: The main generative art platform
- ByX: Curated collection system
- External Builders: Any platform consuming NexArt Code Mode
Integration Example
import { executeCodeMode } from '@nexart/codemode-sdk';
// Execute with explicit VAR values
const result = await executeCodeMode({
source: artistCode,
width: 1950,
height: 2400,
seed: 12345,
vars: [50, 75, 25, 0, 0, 0, 0, 0, 0, 0], // Exactly 10 values
mode: 'static'
});
// Result includes full protocol metadata for verification
const { image, metadata } = result;
console.log(metadata.protocolVersion); // '1.0.0'
console.log(metadata.deterministic); // trueError Handling
All protocol violations throw descriptive errors:
try {
await executeCodeMode({ ... });
} catch (error) {
// "[Code Mode Protocol Error] VAR array must have exactly 10 elements, got 5"
// "[Code Mode Protocol Error] Forbidden pattern: Math.random()"
console.error(error.message);
}Frozen Execution Guarantees — v1.0.0
The following guarantees are LOCKED and will not change in v1.x:
| Guarantee | Description |
|-----------|-------------|
| Determinism | Same code + same seed + same VARs = identical output |
| Static Mode | setup() only, single PNG output |
| Loop Mode | Frame-authoritative, draw() per frame, MP4 output |
| Time Semantics | t ∈ [0,1), frameCount ∈ [0,totalFrames), time in seconds |
| Random | Seeded Mulberry32 PRNG via random() |
| Noise | Seeded Perlin noise via noise() |
| Canvas | Pre-initialized, no createCanvas() |
| VAR | Exactly 10 read-only protocol variables |
| Forbidden Patterns | 13 patterns rejected (see above) |
Future Work (Phase 2+)
- GSL v1 SDK (protocol layer) — separate package
- Extended p5.js compatibility
- WebGL rendering support
- GIF output option
