@nexart/ui-renderer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # @nexart/ui-renderer
2
+
3
+ Version: 0.1.0
4
+
5
+ **Browser-first UI renderer for exploratory generative art.**
6
+
7
+ ---
8
+
9
+ > ⚠️ **IMPORTANT DISCLAIMER**
10
+ >
11
+ > **This SDK is for exploration and UI rendering only.**
12
+ > **Canonical, archival output is produced exclusively by @nexart/codemode-sdk.**
13
+ >
14
+ > This renderer is:
15
+ > - **NOT canonical**
16
+ > - **NOT archival**
17
+ > - **NOT protocol-authoritative**
18
+ >
19
+ > Use it for demos, playgrounds, miniapps, and experimentation.
20
+
21
+ ---
22
+
23
+ ## What This SDK Is
24
+
25
+ A lightweight, browser-first renderer for NexArt-style generative art:
26
+
27
+ - Works in browsers, Vite, React, plain HTML
28
+ - Uses deterministic PRNG for reproducible visuals
29
+ - Renders abstract flow fields, vortices, and orbital motion
30
+ - Zero dependencies
31
+ - No Node.js required
32
+
33
+ ## What This SDK Is NOT
34
+
35
+ - **Not canonical** — Does not produce archival-quality output
36
+ - **Not for NFT minting** — Use `@nexart/codemode-sdk` for production mints
37
+ - **Not protocol-enforcing** — Best-effort determinism only
38
+
39
+ ---
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ npm install @nexart/ui-renderer
45
+ ```
46
+
47
+ Or use directly in HTML:
48
+ ```html
49
+ <script type="module">
50
+ import { createUIRenderer } from 'https://unpkg.com/@nexart/ui-renderer/dist/index.js';
51
+ </script>
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Quick Start
57
+
58
+ ```typescript
59
+ import { createUIRenderer } from '@nexart/ui-renderer';
60
+
61
+ const canvas = document.getElementById('canvas') as HTMLCanvasElement;
62
+
63
+ const renderer = createUIRenderer({
64
+ canvas,
65
+ seed: Date.now(),
66
+ mode: 'static',
67
+ width: 800,
68
+ height: 800,
69
+ });
70
+
71
+ // Render once
72
+ renderer.render();
73
+
74
+ // Or start animation loop
75
+ // renderer.start();
76
+ ```
77
+
78
+ ---
79
+
80
+ ## API
81
+
82
+ ### `createUIRenderer(options)`
83
+
84
+ Create a renderer instance.
85
+
86
+ ```typescript
87
+ const renderer = createUIRenderer({
88
+ canvas: HTMLCanvasElement, // Required: target canvas
89
+ seed: number, // Required: PRNG seed
90
+ mode: 'static' | 'loop', // Required: render mode
91
+ width?: number, // Optional: canvas width (default: canvas.width)
92
+ height?: number, // Optional: canvas height (default: canvas.height)
93
+ });
94
+ ```
95
+
96
+ ### Renderer Methods
97
+
98
+ | Method | Description |
99
+ |--------|-------------|
100
+ | `render()` | Render a single static frame |
101
+ | `start()` | Start animation loop (loop mode) |
102
+ | `stop()` | Stop animation loop |
103
+ | `setSeed(newSeed)` | Change seed and re-render |
104
+ | `destroy()` | Clean up resources |
105
+
106
+ ### Renderer Properties
107
+
108
+ | Property | Value | Description |
109
+ |----------|-------|-------------|
110
+ | `isCanonical` | `false` | Always false - this is not canonical |
111
+ | `isArchival` | `false` | Always false - this is not archival |
112
+
113
+ ---
114
+
115
+ ## Examples
116
+
117
+ ### Static Render
118
+
119
+ ```typescript
120
+ const renderer = createUIRenderer({
121
+ canvas: document.querySelector('canvas'),
122
+ seed: 12345,
123
+ mode: 'static',
124
+ });
125
+
126
+ renderer.render();
127
+ ```
128
+
129
+ ### Animated Loop
130
+
131
+ ```typescript
132
+ const renderer = createUIRenderer({
133
+ canvas: document.querySelector('canvas'),
134
+ seed: Date.now(),
135
+ mode: 'loop',
136
+ });
137
+
138
+ renderer.start();
139
+
140
+ // Stop after 10 seconds
141
+ setTimeout(() => renderer.stop(), 10000);
142
+ ```
143
+
144
+ ### Re-render with New Seed
145
+
146
+ ```typescript
147
+ // Generate new art every 30 seconds
148
+ setInterval(() => {
149
+ renderer.setSeed(Date.now());
150
+ }, 30000);
151
+ ```
152
+
153
+ ---
154
+
155
+ ## Visual Style
156
+
157
+ The renderer produces abstract generative art featuring:
158
+
159
+ - **Flow fields** — Particle traces following noise-based vector fields
160
+ - **Vortex patterns** — Spiral motion around canvas center
161
+ - **Orbital elements** — Dots moving in elliptical paths
162
+ - **Central glow** — Radial gradient for depth
163
+
164
+ Colors are derived from the seed, creating unique palettes per render.
165
+
166
+ ---
167
+
168
+ ## Browser Compatibility
169
+
170
+ Works in all modern browsers:
171
+ - Chrome 80+
172
+ - Firefox 75+
173
+ - Safari 13+
174
+ - Edge 80+
175
+
176
+ No polyfills required.
177
+
178
+ ---
179
+
180
+ ## Comparison with @nexart/codemode-sdk
181
+
182
+ | Feature | @nexart/ui-renderer | @nexart/codemode-sdk |
183
+ |---------|---------------------|----------------------|
184
+ | Environment | Browser only | Node.js / Server |
185
+ | Purpose | Exploration / Demos | Production / NFT minting |
186
+ | Canonical | ❌ No | ✅ Yes |
187
+ | Archival | ❌ No | ✅ Yes |
188
+ | Output | Canvas rendering | PNG / MP4 buffers |
189
+ | Determinism | Best-effort | Strict |
190
+
191
+ ---
192
+
193
+ ## License
194
+
195
+ MIT License
196
+
197
+ Copyright (c) 2024 NexArt
198
+
199
+ ---
200
+
201
+ > **Reminder:** This SDK is about access, not authority.
202
+ > For canonical output, always use `@nexart/codemode-sdk`.
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @nexart/ui-renderer
3
+ * Version: 0.1.0
4
+ *
5
+ * Browser-first UI renderer for exploratory generative art.
6
+ *
7
+ * ⚠️ WARNING: This SDK is for exploration and UI rendering only.
8
+ * Canonical, archival output is produced exclusively by @nexart/codemode-sdk.
9
+ *
10
+ * This renderer is:
11
+ * - NOT canonical
12
+ * - NOT archival
13
+ * - NOT protocol-authoritative
14
+ *
15
+ * Use it for demos, playgrounds, miniapps, and experimentation.
16
+ */
17
+ export { createUIRenderer } from './renderer';
18
+ export type { UIRendererOptions, UIRenderer } from './renderer';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @nexart/ui-renderer
3
+ * Version: 0.1.0
4
+ *
5
+ * Browser-first UI renderer for exploratory generative art.
6
+ *
7
+ * ⚠️ WARNING: This SDK is for exploration and UI rendering only.
8
+ * Canonical, archival output is produced exclusively by @nexart/codemode-sdk.
9
+ *
10
+ * This renderer is:
11
+ * - NOT canonical
12
+ * - NOT archival
13
+ * - NOT protocol-authoritative
14
+ *
15
+ * Use it for demos, playgrounds, miniapps, and experimentation.
16
+ */
17
+ export { createUIRenderer } from './renderer';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * @nexart/ui-renderer - Core Renderer
3
+ * Version: 0.1.0
4
+ *
5
+ * Browser-first generative art renderer.
6
+ * Uses deterministic PRNG for reproducible (but non-canonical) output.
7
+ */
8
+ export interface UIRendererOptions {
9
+ canvas: HTMLCanvasElement;
10
+ seed: number;
11
+ mode: 'static' | 'loop';
12
+ width?: number;
13
+ height?: number;
14
+ }
15
+ export interface UIRenderer {
16
+ render: () => void;
17
+ start: () => void;
18
+ stop: () => void;
19
+ setSeed: (newSeed: number) => void;
20
+ destroy: () => void;
21
+ isCanonical: false;
22
+ isArchival: false;
23
+ }
24
+ /**
25
+ * Create a UI renderer instance.
26
+ *
27
+ * ⚠️ This renderer is NOT canonical and NOT archival.
28
+ * For production NFT minting, use @nexart/codemode-sdk.
29
+ */
30
+ export declare function createUIRenderer(options: UIRendererOptions): UIRenderer;
31
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../src/renderer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC;IACnB,UAAU,EAAE,KAAK,CAAC;CACnB;AAiBD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAgOvE"}
@@ -0,0 +1,210 @@
1
+ /**
2
+ * @nexart/ui-renderer - Core Renderer
3
+ * Version: 0.1.0
4
+ *
5
+ * Browser-first generative art renderer.
6
+ * Uses deterministic PRNG for reproducible (but non-canonical) output.
7
+ */
8
+ /**
9
+ * Deterministic PRNG (Mulberry32)
10
+ * Fast, simple, good distribution
11
+ */
12
+ function createPRNG(seed) {
13
+ let state = seed;
14
+ return () => {
15
+ state |= 0;
16
+ state = (state + 0x6d2b79f5) | 0;
17
+ let t = Math.imul(state ^ (state >>> 15), 1 | state);
18
+ t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
19
+ return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
20
+ };
21
+ }
22
+ /**
23
+ * Create a UI renderer instance.
24
+ *
25
+ * ⚠️ This renderer is NOT canonical and NOT archival.
26
+ * For production NFT minting, use @nexart/codemode-sdk.
27
+ */
28
+ export function createUIRenderer(options) {
29
+ const { canvas, mode } = options;
30
+ let { seed } = options;
31
+ const width = options.width ?? canvas.width ?? 800;
32
+ const height = options.height ?? canvas.height ?? 800;
33
+ canvas.width = width;
34
+ canvas.height = height;
35
+ const ctx = canvas.getContext('2d');
36
+ let animationId = null;
37
+ let frameCount = 0;
38
+ let random;
39
+ // Initialize PRNG
40
+ const initRandom = () => {
41
+ random = createPRNG(seed);
42
+ frameCount = 0;
43
+ };
44
+ initRandom();
45
+ // Utility functions
46
+ const map = (value, start1, stop1, start2, stop2) => {
47
+ return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
48
+ };
49
+ const lerp = (a, b, t) => a + (b - a) * t;
50
+ const noise2D = (x, y) => {
51
+ // Simple value noise using PRNG
52
+ const xi = Math.floor(x);
53
+ const yi = Math.floor(y);
54
+ const xf = x - xi;
55
+ const yf = y - yi;
56
+ const hash = (px, py) => {
57
+ const n = Math.sin(px * 12.9898 + py * 78.233 + seed * 0.001) * 43758.5453;
58
+ return n - Math.floor(n);
59
+ };
60
+ const v00 = hash(xi, yi);
61
+ const v10 = hash(xi + 1, yi);
62
+ const v01 = hash(xi, yi + 1);
63
+ const v11 = hash(xi + 1, yi + 1);
64
+ const sx = xf * xf * (3 - 2 * xf);
65
+ const sy = yf * yf * (3 - 2 * yf);
66
+ return lerp(lerp(v00, v10, sx), lerp(v01, v11, sx), sy);
67
+ };
68
+ /**
69
+ * Render a single frame
70
+ * Style: Abstract flow field / vortex art
71
+ */
72
+ const renderFrame = (t = 0) => {
73
+ // Clear with dark background
74
+ ctx.fillStyle = `hsl(${(seed % 360)}, 15%, 8%)`;
75
+ ctx.fillRect(0, 0, width, height);
76
+ // Re-seed for deterministic frame
77
+ random = createPRNG(seed + frameCount);
78
+ // Draw flow field particles
79
+ const particleCount = 2000;
80
+ const scale = 0.003;
81
+ for (let i = 0; i < particleCount; i++) {
82
+ // Particle position based on PRNG
83
+ let x = random() * width;
84
+ let y = random() * height;
85
+ // Particle color from seed
86
+ const hue = (seed + i * 0.1 + t * 50) % 360;
87
+ const sat = 60 + random() * 30;
88
+ const light = 50 + random() * 30;
89
+ const alpha = 0.3 + random() * 0.4;
90
+ ctx.strokeStyle = `hsla(${hue}, ${sat}%, ${light}%, ${alpha})`;
91
+ ctx.lineWidth = 0.5 + random() * 1.5;
92
+ ctx.beginPath();
93
+ ctx.moveTo(x, y);
94
+ // Trace flow line
95
+ const steps = 20 + Math.floor(random() * 30);
96
+ for (let s = 0; s < steps; s++) {
97
+ // Flow field angle using noise
98
+ const noiseVal = noise2D(x * scale, y * scale + t * 0.5);
99
+ const angle = noiseVal * Math.PI * 4;
100
+ // Add vortex influence
101
+ const cx = width / 2;
102
+ const cy = height / 2;
103
+ const dx = x - cx;
104
+ const dy = y - cy;
105
+ const dist = Math.sqrt(dx * dx + dy * dy);
106
+ const vortexStrength = map(dist, 0, width / 2, 0.8, 0.2);
107
+ const vortexAngle = Math.atan2(dy, dx) + Math.PI / 2;
108
+ const finalAngle = lerp(angle, vortexAngle, vortexStrength);
109
+ x += Math.cos(finalAngle) * 3;
110
+ y += Math.sin(finalAngle) * 3;
111
+ ctx.lineTo(x, y);
112
+ // Stop if out of bounds
113
+ if (x < 0 || x > width || y < 0 || y > height)
114
+ break;
115
+ }
116
+ ctx.stroke();
117
+ }
118
+ // Draw orbital elements
119
+ const orbits = 3 + Math.floor(random() * 4);
120
+ for (let o = 0; o < orbits; o++) {
121
+ const orbitRadius = 100 + random() * (Math.min(width, height) / 3);
122
+ const orbitSpeed = 0.5 + random() * 1.5;
123
+ const dotCount = 20 + Math.floor(random() * 40);
124
+ const orbitHue = (seed + o * 60 + t * 30) % 360;
125
+ for (let d = 0; d < dotCount; d++) {
126
+ const angle = (d / dotCount) * Math.PI * 2 + t * orbitSpeed + o;
127
+ const wobble = Math.sin(angle * 3 + t * 2) * 20;
128
+ const x = width / 2 + Math.cos(angle) * (orbitRadius + wobble);
129
+ const y = height / 2 + Math.sin(angle) * (orbitRadius + wobble);
130
+ const size = 2 + random() * 4;
131
+ const alpha = 0.4 + random() * 0.4;
132
+ ctx.beginPath();
133
+ ctx.arc(x, y, size, 0, Math.PI * 2);
134
+ ctx.fillStyle = `hsla(${orbitHue}, 70%, 60%, ${alpha})`;
135
+ ctx.fill();
136
+ }
137
+ }
138
+ // Central glow
139
+ const gradient = ctx.createRadialGradient(width / 2, height / 2, 0, width / 2, height / 2, width / 3);
140
+ const glowHue = (seed + t * 20) % 360;
141
+ gradient.addColorStop(0, `hsla(${glowHue}, 80%, 60%, 0.15)`);
142
+ gradient.addColorStop(0.5, `hsla(${glowHue}, 60%, 40%, 0.05)`);
143
+ gradient.addColorStop(1, 'transparent');
144
+ ctx.fillStyle = gradient;
145
+ ctx.fillRect(0, 0, width, height);
146
+ frameCount++;
147
+ };
148
+ /**
149
+ * Render static image
150
+ */
151
+ const render = () => {
152
+ initRandom();
153
+ renderFrame(0);
154
+ };
155
+ /**
156
+ * Start animation loop
157
+ */
158
+ const start = () => {
159
+ if (mode !== 'loop') {
160
+ console.warn('start() called but mode is "static". Use render() instead.');
161
+ render();
162
+ return;
163
+ }
164
+ stop();
165
+ initRandom();
166
+ let startTime = performance.now();
167
+ const loop = () => {
168
+ const elapsed = (performance.now() - startTime) / 1000;
169
+ const t = (elapsed % 10) / 10; // 10 second loop
170
+ renderFrame(t);
171
+ animationId = requestAnimationFrame(loop);
172
+ };
173
+ animationId = requestAnimationFrame(loop);
174
+ };
175
+ /**
176
+ * Stop animation loop
177
+ */
178
+ const stop = () => {
179
+ if (animationId !== null) {
180
+ cancelAnimationFrame(animationId);
181
+ animationId = null;
182
+ }
183
+ };
184
+ /**
185
+ * Set new seed and re-render
186
+ */
187
+ const setSeed = (newSeed) => {
188
+ seed = newSeed;
189
+ initRandom();
190
+ if (mode === 'static' || animationId === null) {
191
+ render();
192
+ }
193
+ };
194
+ /**
195
+ * Clean up resources
196
+ */
197
+ const destroy = () => {
198
+ stop();
199
+ ctx.clearRect(0, 0, width, height);
200
+ };
201
+ return {
202
+ render,
203
+ start,
204
+ stop,
205
+ setSeed,
206
+ destroy,
207
+ isCanonical: false,
208
+ isArchival: false,
209
+ };
210
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@nexart/ui-renderer",
3
+ "version": "0.1.0",
4
+ "description": "Browser-first UI renderer for exploratory generative art (non-canonical)",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "type": "module",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.0.0"
18
+ },
19
+ "keywords": [
20
+ "generative-art",
21
+ "canvas",
22
+ "browser",
23
+ "renderer",
24
+ "exploratory",
25
+ "creative-coding"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/artnames/nexart-ui-renderer.git"
30
+ },
31
+ "author": "NexArt",
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }