@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 +202 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/renderer.d.ts +31 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +210 -0
- package/package.json +35 -0
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`.
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/renderer.js
ADDED
|
@@ -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
|
+
}
|