@nexart/codemode-sdk 1.8.3 → 1.8.4
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/CHANGELOG.md +31 -0
- package/README.md +2 -2
- package/dist/cjs/browser.cjs +3077 -0
- package/dist/cjs/browser.js +3042 -0
- package/dist/cjs/core.cjs +1660 -0
- package/dist/cjs/core.js +1636 -0
- package/dist/cjs/node.cjs +3245 -0
- package/dist/cjs/node.js +3208 -0
- package/dist/esm/browser.cjs +3077 -0
- package/dist/esm/browser.js +3042 -0
- package/dist/esm/core.cjs +1660 -0
- package/dist/esm/core.js +1636 -0
- package/dist/esm/node.cjs +3245 -0
- package/dist/esm/node.js +3208 -0
- package/dist/types/sdk/codemode/builder-manifest.d.ts.map +1 -0
- package/dist/{sdk → types/sdk}/codemode/core-index.d.ts +1 -1
- package/dist/types/sdk/codemode/core-index.d.ts.map +1 -0
- package/dist/types/sdk/codemode/engine.d.ts.map +1 -0
- package/dist/{sdk → types/sdk}/codemode/entry/browser.d.ts +1 -1
- package/dist/types/sdk/codemode/entry/browser.d.ts.map +1 -0
- package/dist/types/sdk/codemode/entry/node.d.ts.map +1 -0
- package/dist/types/sdk/codemode/execute.d.ts.map +1 -0
- package/dist/types/sdk/codemode/execution-sandbox.d.ts.map +1 -0
- package/dist/types/sdk/codemode/loop-engine.d.ts.map +1 -0
- package/dist/types/sdk/codemode/p5-runtime.d.ts.map +1 -0
- package/dist/{sdk → types/sdk}/codemode/runtime.d.ts +1 -1
- package/dist/types/sdk/codemode/runtime.d.ts.map +1 -0
- package/dist/types/sdk/codemode/snapshot.d.ts +72 -0
- package/dist/types/sdk/codemode/snapshot.d.ts.map +1 -0
- package/dist/types/sdk/codemode/sound-bridge.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-engine.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/index.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/isoflow.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/orb.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/rings.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/squares.d.ts.map +1 -0
- package/dist/types/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +1 -0
- package/dist/types/sdk/codemode/static-engine.d.ts.map +1 -0
- package/dist/types/sdk/codemode/types.d.ts.map +1 -0
- package/dist/{sdk → types/sdk}/codemode/version.d.ts +2 -2
- package/dist/types/sdk/codemode/version.d.ts.map +1 -0
- package/dist/types/shared/soundSnapshot.d.ts.map +1 -0
- package/package.json +22 -22
- package/dist/sdk/codemode/builder-manifest.d.ts.map +0 -1
- package/dist/sdk/codemode/builder-manifest.js +0 -97
- package/dist/sdk/codemode/core-index.d.ts.map +0 -1
- package/dist/sdk/codemode/core-index.js +0 -28
- package/dist/sdk/codemode/engine.d.ts.map +0 -1
- package/dist/sdk/codemode/engine.js +0 -67
- package/dist/sdk/codemode/entry/browser.d.ts.map +0 -1
- package/dist/sdk/codemode/entry/browser.js +0 -69
- package/dist/sdk/codemode/entry/node.d.ts.map +0 -1
- package/dist/sdk/codemode/entry/node.js +0 -35
- package/dist/sdk/codemode/execute.d.ts.map +0 -1
- package/dist/sdk/codemode/execute.js +0 -283
- package/dist/sdk/codemode/execution-sandbox.d.ts.map +0 -1
- package/dist/sdk/codemode/execution-sandbox.js +0 -207
- package/dist/sdk/codemode/loop-engine.d.ts.map +0 -1
- package/dist/sdk/codemode/loop-engine.js +0 -229
- package/dist/sdk/codemode/p5-runtime.d.ts.map +0 -1
- package/dist/sdk/codemode/p5-runtime.js +0 -1033
- package/dist/sdk/codemode/runtime.d.ts.map +0 -1
- package/dist/sdk/codemode/runtime.js +0 -220
- package/dist/sdk/codemode/sound-bridge.d.ts.map +0 -1
- package/dist/sdk/codemode/sound-bridge.js +0 -128
- package/dist/sdk/codemode/soundart-engine.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-engine.js +0 -173
- package/dist/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/chladniBloom.js +0 -53
- package/dist/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/dualVortex.js +0 -67
- package/dist/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/geometryIllusion.js +0 -89
- package/dist/sdk/codemode/soundart-sketches/index.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/index.js +0 -72
- package/dist/sdk/codemode/soundart-sketches/isoflow.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/isoflow.js +0 -60
- package/dist/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/loomWeave.js +0 -59
- package/dist/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/noiseTerraces.js +0 -53
- package/dist/sdk/codemode/soundart-sketches/orb.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/orb.js +0 -50
- package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.js +0 -72
- package/dist/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/prismFlowFields.js +0 -51
- package/dist/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/radialBurst.js +0 -60
- package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.js +0 -89
- package/dist/sdk/codemode/soundart-sketches/rings.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/rings.js +0 -89
- package/dist/sdk/codemode/soundart-sketches/squares.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/squares.js +0 -52
- package/dist/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +0 -1
- package/dist/sdk/codemode/soundart-sketches/waveStripes.js +0 -44
- package/dist/sdk/codemode/static-engine.d.ts.map +0 -1
- package/dist/sdk/codemode/static-engine.js +0 -157
- package/dist/sdk/codemode/types.d.ts.map +0 -1
- package/dist/sdk/codemode/types.js +0 -34
- package/dist/sdk/codemode/version.d.ts.map +0 -1
- package/dist/sdk/codemode/version.js +0 -17
- package/dist/shared/soundSnapshot.d.ts.map +0 -1
- package/dist/shared/soundSnapshot.js +0 -128
- /package/dist/{sdk → types/sdk}/codemode/builder-manifest.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/engine.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/entry/node.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/execute.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/execution-sandbox.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/loop-engine.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/p5-runtime.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/sound-bridge.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-engine.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/chladniBloom.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/dualVortex.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/geometryIllusion.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/index.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/isoflow.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/loomWeave.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/noiseTerraces.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/orb.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/pixelGlyphs.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/prismFlowFields.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/radialBurst.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/resonantSoundBodies.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/rings.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/squares.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/soundart-sketches/waveStripes.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/static-engine.d.ts +0 -0
- /package/dist/{sdk → types/sdk}/codemode/types.d.ts +0 -0
- /package/dist/{shared → types/shared}/soundSnapshot.d.ts +0 -0
|
@@ -0,0 +1,3245 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
24
|
+
|
|
25
|
+
// soundart-sketches/rings.ts
|
|
26
|
+
var RINGS_SKETCH;
|
|
27
|
+
var init_rings = __esm({
|
|
28
|
+
"soundart-sketches/rings.ts"() {
|
|
29
|
+
RINGS_SKETCH = `
|
|
30
|
+
// Flow Grid Rings - SoundArt Style
|
|
31
|
+
// Uses S.* sound globals and standard p5-like functions
|
|
32
|
+
|
|
33
|
+
function setup() {
|
|
34
|
+
// Set background based on mode (rgb derived from sound, black, or white)
|
|
35
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
36
|
+
|
|
37
|
+
if (bgMode === 'white') {
|
|
38
|
+
background(245, 245, 245);
|
|
39
|
+
} else if (bgMode === 'black') {
|
|
40
|
+
background(10, 10, 10);
|
|
41
|
+
} else {
|
|
42
|
+
// Deterministic RGB background derived from sound snapshot
|
|
43
|
+
// Previously used clock time, now uses sound parameters for reproducibility
|
|
44
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
45
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
46
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
47
|
+
background(r, g, b);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Grid dimensions based on sound volume
|
|
51
|
+
const cols = Math.floor(map(S.volume, 0, 100, 2, 32));
|
|
52
|
+
const rows = Math.floor(cols * 1.5);
|
|
53
|
+
const margin = map(S.length, 0, 100, width * 0.05, width * 0.1);
|
|
54
|
+
const innerW = width - margin * 2;
|
|
55
|
+
const innerH = height - margin * 2;
|
|
56
|
+
const cellW = innerW / cols;
|
|
57
|
+
const cellH = innerH / rows;
|
|
58
|
+
const gap = Math.min(cellW, cellH) * 0.25;
|
|
59
|
+
const maxRadius = (Math.min(cellW, cellH) - gap) / 2;
|
|
60
|
+
|
|
61
|
+
// Palette Harmony based on sound
|
|
62
|
+
const mainHue = (S.brightness * 3.6) % 360;
|
|
63
|
+
const hueShift = map(S.harmonicity, 0, 100, 20, 60);
|
|
64
|
+
const sat = map(S.treble, 0, 100, 60, 90);
|
|
65
|
+
const bri = map(S.bass, 0, 100, 70, 95);
|
|
66
|
+
|
|
67
|
+
// Generate palette
|
|
68
|
+
const palette = [
|
|
69
|
+
hslColor(mainHue, sat, bri),
|
|
70
|
+
hslColor((mainHue + hueShift) % 360, sat - 10, bri + 5),
|
|
71
|
+
hslColor((mainHue - hueShift + 360) % 360, sat - 10, bri + 5),
|
|
72
|
+
hslColor((mainHue + 180) % 360, sat - 20, bri - 10),
|
|
73
|
+
hslColor((mainHue + 120) % 360, sat, bri - 5),
|
|
74
|
+
hslColor((mainHue + 240) % 360, sat, bri - 5),
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
// Flow field parameters
|
|
78
|
+
const noiseScale = 0.15;
|
|
79
|
+
const noiseStrength = map(S.aggression, 0, 100, 0.5, 4.0);
|
|
80
|
+
const jitter = map(S.rhythmicity, 0, 100, 0, 0.15);
|
|
81
|
+
const ringBias = map(S.attack, 0, 100, 0.85, 1.15);
|
|
82
|
+
|
|
83
|
+
noStroke();
|
|
84
|
+
|
|
85
|
+
// Render Grid
|
|
86
|
+
for (let gx = 0; gx < cols; gx++) {
|
|
87
|
+
for (let gy = 0; gy < rows; gy++) {
|
|
88
|
+
const cx = margin + gx * cellW + cellW / 2 + (random() - 0.5) * jitter * cellW;
|
|
89
|
+
const cy = margin + gy * cellH + cellH / 2 + (random() - 0.5) * jitter * cellH;
|
|
90
|
+
|
|
91
|
+
const n = noise(gx * noiseScale, gy * noiseScale);
|
|
92
|
+
const angle = n * PI * 2 * noiseStrength;
|
|
93
|
+
const rings = Math.max(1, Math.floor(map(sin(angle), -1, 1, 1, 6)));
|
|
94
|
+
const step = (maxRadius / rings) * ringBias * 0.9;
|
|
95
|
+
|
|
96
|
+
for (let i = rings; i > 0; i--) {
|
|
97
|
+
fill(palette[(rings - i) % palette.length]);
|
|
98
|
+
ellipse(cx, cy, i * step * 2, i * step * 2);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Helper function for HSL colors
|
|
105
|
+
function hslColor(h, s, l) {
|
|
106
|
+
return 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// soundart-sketches/pixelGlyphs.ts
|
|
113
|
+
var PIXEL_GLYPHS_SKETCH;
|
|
114
|
+
var init_pixelGlyphs = __esm({
|
|
115
|
+
"soundart-sketches/pixelGlyphs.ts"() {
|
|
116
|
+
PIXEL_GLYPHS_SKETCH = `
|
|
117
|
+
function setup() {
|
|
118
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
119
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
120
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
121
|
+
else {
|
|
122
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
123
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
124
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
125
|
+
background(r, g, b);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const cols = Math.floor(map(S.length, 0, 100, 2, 18));
|
|
129
|
+
const rows = Math.floor(map(S.length, 0, 100, 3, 24));
|
|
130
|
+
const density = map(S.volume, 0, 100, 0.25, 0.6);
|
|
131
|
+
const jitter = map(S.attack, 0, 100, 0, 0.25);
|
|
132
|
+
const sparkRate = map(S.treble, 0, 100, 0.05, 0.45);
|
|
133
|
+
const invertRate = map(S.dynamicRange, 0, 100, 0.05, 0.35);
|
|
134
|
+
|
|
135
|
+
const margin = 140, gutter = 20, glyphRes = 7;
|
|
136
|
+
const P = [[11,16,38],[255,51,102],[0,224,255],[255,209,102],[124,255,178],[138,43,226]];
|
|
137
|
+
const cellW = (width - margin*2 - gutter*(cols - 1)) / cols;
|
|
138
|
+
const cellH = (height - margin*2 - gutter*(rows - 1)) / rows;
|
|
139
|
+
if (cellW <= 0 || cellH <= 0) return;
|
|
140
|
+
|
|
141
|
+
noStroke();
|
|
142
|
+
|
|
143
|
+
for (let gy = 0; gy < rows; gy++) {
|
|
144
|
+
for (let gx = 0; gx < cols; gx++) {
|
|
145
|
+
const cx = margin + gx * (cellW + gutter);
|
|
146
|
+
const cy = margin + gy * (cellH + gutter);
|
|
147
|
+
const invert = noise(gx * 0.4, gy * 0.3) < invertRate;
|
|
148
|
+
const baseA = map(S.amplitude, 0, 100, 0.8, 1.0);
|
|
149
|
+
const rndVal = noise(gx * 0.5, gy * 0.7);
|
|
150
|
+
const baseIdx = 1 + Math.floor(rndVal * 5);
|
|
151
|
+
const accentIdx = 1 + Math.floor(noise(gx + 3.3, gy + 4.4) * 5);
|
|
152
|
+
const baseCol = invert ? [240, 245, 255] : P[baseIdx];
|
|
153
|
+
const accCol = invert ? P[baseIdx] : P[accentIdx];
|
|
154
|
+
|
|
155
|
+
const px = Math.floor((cellW * 0.84) / glyphRes);
|
|
156
|
+
const py = Math.floor((cellH * 0.84) / glyphRes);
|
|
157
|
+
const gw = px * glyphRes, gh = py * glyphRes;
|
|
158
|
+
const ox = cx + Math.floor((cellW - gw) * 0.5);
|
|
159
|
+
const oy = cy + Math.floor((cellH - gh) * 0.5);
|
|
160
|
+
|
|
161
|
+
for (let j = 0; j < glyphRes; j++) {
|
|
162
|
+
for (let i = 0; i < Math.ceil(glyphRes / 2); i++) {
|
|
163
|
+
const pNoise = noise(gx * 0.51 + i * 0.19, gy * 0.47 + j * 0.21);
|
|
164
|
+
const p = density + (pNoise - 0.5) * 0.35;
|
|
165
|
+
if (random() < constrain(p, 0.05, 0.9)) {
|
|
166
|
+
const jx = Math.floor((random() - 0.5) * jitter * px);
|
|
167
|
+
const jy = Math.floor((random() - 0.5) * jitter * py);
|
|
168
|
+
const x0 = ox + i * px + jx;
|
|
169
|
+
const y0 = oy + j * py + jy;
|
|
170
|
+
|
|
171
|
+
fill(baseCol[0], baseCol[1], baseCol[2], baseA * 255);
|
|
172
|
+
rect(x0, y0, px, py);
|
|
173
|
+
const mi = glyphRes - 1 - i;
|
|
174
|
+
rect(ox + mi * px - jx, y0, px, py);
|
|
175
|
+
|
|
176
|
+
if (random() < sparkRate) {
|
|
177
|
+
fill(accCol[0], accCol[1], accCol[2], baseA * 255);
|
|
178
|
+
rect(x0 + px * 0.2, y0 + py * 0.2, px * 0.6, py * 0.6);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
`;
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// soundart-sketches/radialBurst.ts
|
|
191
|
+
var RADIAL_BURST_SKETCH;
|
|
192
|
+
var init_radialBurst = __esm({
|
|
193
|
+
"soundart-sketches/radialBurst.ts"() {
|
|
194
|
+
RADIAL_BURST_SKETCH = `
|
|
195
|
+
function setup() {
|
|
196
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
197
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
198
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
199
|
+
else {
|
|
200
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
201
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
202
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
203
|
+
background(r, g, b);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const centerX = width / 2;
|
|
207
|
+
const centerY = height / 2;
|
|
208
|
+
const baseN = Math.floor(map(S.rhythmicity, 0, 100, 90, 320));
|
|
209
|
+
const rhythmMask = 0.4 + (S.rhythmicity / 100) * 0.6;
|
|
210
|
+
|
|
211
|
+
const palette = [];
|
|
212
|
+
const mainHue = (S.brightness * 3.6) % 360;
|
|
213
|
+
for (let i = 0; i < 16; i++) {
|
|
214
|
+
const h = (mainHue + i * 22.5) % 360;
|
|
215
|
+
const sat = map(S.treble, 0, 100, 60, 90);
|
|
216
|
+
const bri = map(S.bass, 0, 100, 70, 95);
|
|
217
|
+
palette.push('hsl(' + h + ',' + sat + '%,' + bri + '%)');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
noFill();
|
|
221
|
+
|
|
222
|
+
for (let i = 0; i < baseN; i++) {
|
|
223
|
+
if ((i % 3) && random() > rhythmMask) continue;
|
|
224
|
+
|
|
225
|
+
const angle = (i / baseN) * PI * 2;
|
|
226
|
+
const col = palette[i % palette.length];
|
|
227
|
+
|
|
228
|
+
const lenJit = map(S.dynamicRange, 0, 100, 0, height * 2.45);
|
|
229
|
+
const rayLength = (S.amplitude / 100) * (height * 1.65) + random() * lenJit;
|
|
230
|
+
|
|
231
|
+
const waveAmp = map(S.harmonicity, 0, 100, 0, 42);
|
|
232
|
+
const waveFreq = map(S.treble, 0, 100, 1, 48);
|
|
233
|
+
const segments = 28;
|
|
234
|
+
|
|
235
|
+
const thickness = 1.2 + (S.attack / 100) * 7 + (S.aggression / 100) * 5 + sin(i * 0.9) * 1.4;
|
|
236
|
+
|
|
237
|
+
stroke(col);
|
|
238
|
+
strokeWeight(thickness);
|
|
239
|
+
|
|
240
|
+
beginShape();
|
|
241
|
+
for (let s = 0; s <= segments; s++) {
|
|
242
|
+
const t = s / segments;
|
|
243
|
+
const radius = t * rayLength;
|
|
244
|
+
const wobble = sin(t * PI * waveFreq + i * 0.2) * waveAmp * (1 - t);
|
|
245
|
+
const wx = centerX + cos(angle + wobble * 0.01) * radius;
|
|
246
|
+
const wy = centerY + sin(angle + wobble * 0.01) * radius;
|
|
247
|
+
vertex(wx, wy);
|
|
248
|
+
}
|
|
249
|
+
endShape();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
`;
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
// soundart-sketches/orb.ts
|
|
257
|
+
var ORB_SKETCH;
|
|
258
|
+
var init_orb = __esm({
|
|
259
|
+
"soundart-sketches/orb.ts"() {
|
|
260
|
+
ORB_SKETCH = `
|
|
261
|
+
function setup() {
|
|
262
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
263
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
264
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
265
|
+
else {
|
|
266
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
267
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
268
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
269
|
+
background(r, g, b);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const cx = width / 2 + map(S.dynamicRange, 0, 100, -150, 150);
|
|
273
|
+
const cy = height / 2 + map(S.rhythmicity, 0, 100, -100, 100);
|
|
274
|
+
const baseRadius = map(S.length, 0, 100, 240, 1200);
|
|
275
|
+
const steps = Math.round(map(S.rhythmicity, 0, 100, 280, 400));
|
|
276
|
+
const baseHue = map(S.hue, 0, 100, 0, 360);
|
|
277
|
+
const hue1 = baseHue;
|
|
278
|
+
const hue2 = (baseHue + map(S.attack, 0, 100, 80, 180)) % 360;
|
|
279
|
+
|
|
280
|
+
noStroke();
|
|
281
|
+
|
|
282
|
+
for (let i = steps; i > 0; i--) {
|
|
283
|
+
const pct = i / steps;
|
|
284
|
+
const r = baseRadius * pct * map(S.aggression, 0, 100, 0.92, 1.06);
|
|
285
|
+
const t = pow(pct, map(S.brightness, 0, 100, 1.8, 2.8));
|
|
286
|
+
const h = (lerp(hue1, hue2, t) + pct * map(S.harmonicity, 0, 100, -15, 15) + sin(pct * PI * 3) * map(S.brightness, 0, 100, -12, 12)) % 360;
|
|
287
|
+
const sat = constrain(70 + map(S.treble, 0, 100, -20, 20), 0, 100);
|
|
288
|
+
const bri = constrain(lerp(90, 30, t) + map(S.brightness, 0, 100, -6, 4), 0, 100);
|
|
289
|
+
const alpha = (map(pct, 1, 0, 18, 70) + sin(i * 0.15) * map(S.treble, 0, 100, 1, 4)) * map(S.volume, 0, 100, 0.8, 1.1) * 1.2;
|
|
290
|
+
|
|
291
|
+
fill(hslColor(h, sat, bri, constrain(alpha / 100, 0, 1)));
|
|
292
|
+
|
|
293
|
+
const rx = r * map(S.aggression, 0, 100, 0.9, 1.05);
|
|
294
|
+
const ry = r * map(S.aggression, 0, 100, 0.9, 1.05);
|
|
295
|
+
const angle = pct * PI * map(S.rhythmicity, 0, 100, 2, 4);
|
|
296
|
+
const wobbleAmp = map(S.aggression, 0, 100, 10, 40);
|
|
297
|
+
const wobbleX = sin(angle) * wobbleAmp;
|
|
298
|
+
const wobbleY = cos(angle) * wobbleAmp;
|
|
299
|
+
|
|
300
|
+
ellipse(cx + wobbleX, cy + wobbleY, rx * 2, ry * 2);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
function hslColor(h, s, l, a) {
|
|
305
|
+
if (a !== undefined) return 'hsla(' + h + ',' + s + '%,' + l + '%,' + a + ')';
|
|
306
|
+
return 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
|
307
|
+
}
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// soundart-sketches/waveStripes.ts
|
|
313
|
+
var WAVE_STRIPES_SKETCH;
|
|
314
|
+
var init_waveStripes = __esm({
|
|
315
|
+
"soundart-sketches/waveStripes.ts"() {
|
|
316
|
+
WAVE_STRIPES_SKETCH = `
|
|
317
|
+
function setup() {
|
|
318
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
319
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
320
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
321
|
+
else {
|
|
322
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
323
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
324
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
325
|
+
background(r, g, b);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const numLines = Math.floor(map(S.length, 0, 100, 5, 100));
|
|
329
|
+
const amp = map(S.volume, 0, 100, 0.012, 0.09);
|
|
330
|
+
const lineNoise = map(S.harmonicity, 0, 100, 0.1, 0.8);
|
|
331
|
+
const freqNoise = map(S.aggression, 0, 100, 0.004, 0.02);
|
|
332
|
+
const waveFreq = map(S.rhythmicity, 0, 100, 0.0003, 0.003);
|
|
333
|
+
const strokeW = map(S.bass, 0, 100, 2, 6.0);
|
|
334
|
+
const vividBoost = constrain(map(S.brightness, 0, 100, 0.6, 1.2), 0.6, 1.4);
|
|
335
|
+
|
|
336
|
+
noFill();
|
|
337
|
+
strokeWeight(strokeW);
|
|
338
|
+
|
|
339
|
+
for (let j = 0; j < numLines; j++) {
|
|
340
|
+
const yBase = map(j, 0, numLines - 1, 0.05 * height, 0.95 * height);
|
|
341
|
+
const lineTreble = constrain(S.treble + noise(j, 99) * 20 - 10, 0, 100);
|
|
342
|
+
const hue = ((map(lineTreble, 60, 100, 0, 360) + map(S.attack, 0, 100, 0, 120) + noise(j, 100) * 20 - 10) % 360 + 360) % 360;
|
|
343
|
+
|
|
344
|
+
stroke('hsl(' + hue + ',85%,' + constrain(vividBoost * 55, 40, 70) + '%)');
|
|
345
|
+
|
|
346
|
+
beginShape();
|
|
347
|
+
const pointSpacing = constrain(map(S.harmonicity, 0, 100, 20, 4), 4, 20);
|
|
348
|
+
for (let i = 0; i <= width; i += pointSpacing) {
|
|
349
|
+
const shimmer = sin(i * 0.05 + j * 0.1) * 0.05;
|
|
350
|
+
const offsetY = sin(i * waveFreq + j * 0.2 + shimmer) * amp * height;
|
|
351
|
+
const localNoiseVal = (noise(i * freqNoise, j * lineNoise) - 0.5) * amp * height;
|
|
352
|
+
const y = yBase + offsetY + localNoiseVal;
|
|
353
|
+
vertex(i, y);
|
|
354
|
+
}
|
|
355
|
+
endShape();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
`;
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
// soundart-sketches/squares.ts
|
|
363
|
+
var SQUARES_SKETCH;
|
|
364
|
+
var init_squares = __esm({
|
|
365
|
+
"soundart-sketches/squares.ts"() {
|
|
366
|
+
SQUARES_SKETCH = `
|
|
367
|
+
function setup() {
|
|
368
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
369
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
370
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
371
|
+
else {
|
|
372
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
373
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
374
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
375
|
+
background(r, g, b);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
const numSquares = Math.floor(map(S.length, 0, 100, 40, 300));
|
|
379
|
+
let size = Math.min(width, height) * 0.9;
|
|
380
|
+
const shrinkFactor = map(S.dynamicRange, 0, 100, 0.92, 1.08);
|
|
381
|
+
const rotationStep = map(S.aggression, 0, 100, 0.01, 0.07);
|
|
382
|
+
const strokeMin = map(S.volume, 0, 100, 1.0, 6.0);
|
|
383
|
+
const strokeMax = map(S.bass, 0, 100, 2.0, 8.0);
|
|
384
|
+
|
|
385
|
+
const colors = [];
|
|
386
|
+
for (let i = 0; i < 5; i++) {
|
|
387
|
+
const baseHue = (S.hue + i * 60 + random() * 20) % 360;
|
|
388
|
+
const sat = constrain(map(S.treble, 0, 100, 60, 95), 50, 100);
|
|
389
|
+
const bri = constrain(map(S.brightness, 0, 100, 40, 85), 30, 90);
|
|
390
|
+
colors.push(hslColor(baseHue, sat, bri));
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
noFill();
|
|
394
|
+
translate(width / 2, height / 2);
|
|
395
|
+
|
|
396
|
+
let angle = 0;
|
|
397
|
+
for (let i = 0; i < numSquares; i++) {
|
|
398
|
+
const strokeW = strokeMin + (i / numSquares) * (strokeMax - strokeMin);
|
|
399
|
+
strokeWeight(strokeW);
|
|
400
|
+
stroke(colors[i % colors.length]);
|
|
401
|
+
|
|
402
|
+
push();
|
|
403
|
+
rotate(angle);
|
|
404
|
+
rect(-size / 2, -size / 2, size, size);
|
|
405
|
+
pop();
|
|
406
|
+
|
|
407
|
+
angle += rotationStep;
|
|
408
|
+
size *= shrinkFactor;
|
|
409
|
+
if (size < 4) break;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function hslColor(h, s, l) {
|
|
414
|
+
return 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
|
415
|
+
}
|
|
416
|
+
`;
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// soundart-sketches/loomWeave.ts
|
|
421
|
+
var LOOM_WEAVE_SKETCH;
|
|
422
|
+
var init_loomWeave = __esm({
|
|
423
|
+
"soundart-sketches/loomWeave.ts"() {
|
|
424
|
+
LOOM_WEAVE_SKETCH = `
|
|
425
|
+
function setup() {
|
|
426
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
427
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
428
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
429
|
+
else {
|
|
430
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
431
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
432
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
433
|
+
background(r, g, b);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const threadsX = Math.round(map(S.rhythmicity, 0, 100, 4, 22));
|
|
437
|
+
const threadsY = Math.round(map(S.volume, 0, 100, 4, 22));
|
|
438
|
+
const margin = map(S.dynamicRange, 0, 100, 80, 220);
|
|
439
|
+
const threadW = map(S.amplitude, 0, 100, 8, 26);
|
|
440
|
+
const noiseFreq = map(S.harmonicity, 0, 100, 0.012, 0.035);
|
|
441
|
+
const noiseAmp = map(S.aggression, 0, 100, 8, 38);
|
|
442
|
+
const hueBase = map(S.hue, 0, 100, 0, 360);
|
|
443
|
+
const warpHue = hueBase;
|
|
444
|
+
const weftHue = (hueBase + 180) % 360;
|
|
445
|
+
const brightness = map(S.brightness, 0, 100, 45, 75);
|
|
446
|
+
const chroma = map(S.chroma, 0, 100, 70, 95);
|
|
447
|
+
const alphaVal = map(S.attack, 0, 100, 0.8, 1.0);
|
|
448
|
+
|
|
449
|
+
noStroke();
|
|
450
|
+
|
|
451
|
+
for (let i = 0; i < threadsX; i++) {
|
|
452
|
+
const xBase = margin + (width - 2 * margin) * (i / max(1, threadsX - 1));
|
|
453
|
+
fill('hsla(' + warpHue + ',' + chroma + '%,' + brightness + '%,' + alphaVal + ')');
|
|
454
|
+
beginShape();
|
|
455
|
+
for (let y = 0; y <= height; y += 16) {
|
|
456
|
+
const dx = map(noise(i * 0.25, y * noiseFreq), 0, 1, -noiseAmp, noiseAmp);
|
|
457
|
+
vertex(xBase + dx - threadW / 2, y);
|
|
458
|
+
}
|
|
459
|
+
for (let y = height; y >= 0; y -= 16) {
|
|
460
|
+
const dx = map(noise(i * 0.37, y * noiseFreq + 200), 0, 1, -noiseAmp, noiseAmp);
|
|
461
|
+
vertex(xBase + dx + threadW / 2, y);
|
|
462
|
+
}
|
|
463
|
+
endShape(CLOSE);
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
for (let j = 0; j < threadsY; j++) {
|
|
467
|
+
const yBase = margin + (height - 2 * margin) * (j / max(1, threadsY - 1));
|
|
468
|
+
fill('hsla(' + weftHue + ',' + chroma + '%,' + brightness + '%,' + alphaVal + ')');
|
|
469
|
+
beginShape();
|
|
470
|
+
for (let x = 0; x <= width; x += 16) {
|
|
471
|
+
const dy = map(noise(j * 0.25, x * noiseFreq), 0, 1, -noiseAmp, noiseAmp);
|
|
472
|
+
vertex(x, yBase + dy - threadW / 2);
|
|
473
|
+
}
|
|
474
|
+
for (let x = width; x >= 0; x -= 16) {
|
|
475
|
+
const dy = map(noise(j * 0.37, x * noiseFreq + 200), 0, 1, -noiseAmp, noiseAmp);
|
|
476
|
+
vertex(x, yBase + dy + threadW / 2);
|
|
477
|
+
}
|
|
478
|
+
endShape(CLOSE);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
// soundart-sketches/noiseTerraces.ts
|
|
486
|
+
var NOISE_TERRACES_SKETCH;
|
|
487
|
+
var init_noiseTerraces = __esm({
|
|
488
|
+
"soundart-sketches/noiseTerraces.ts"() {
|
|
489
|
+
NOISE_TERRACES_SKETCH = `
|
|
490
|
+
function setup() {
|
|
491
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
492
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
493
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
494
|
+
else {
|
|
495
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
496
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
497
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
498
|
+
background(r, g, b);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const levels = Math.floor(map(S.harmonicity, 0, 100, 6, 16));
|
|
502
|
+
const res = Math.floor(map(S.brightness, 0, 100, 3, 10));
|
|
503
|
+
|
|
504
|
+
const mainHue = (S.brightness * 3.6) % 360;
|
|
505
|
+
const palette = [];
|
|
506
|
+
for (let i = 0; i < 16; i++) {
|
|
507
|
+
const h = (mainHue + i * 22.5) % 360;
|
|
508
|
+
const sat = map(S.treble, 0, 100, 60, 90);
|
|
509
|
+
const bri = map(S.bass, 0, 100, 70, 95);
|
|
510
|
+
palette.push([h, sat, bri]);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
noStroke();
|
|
514
|
+
|
|
515
|
+
for (let lvl = 0; lvl < levels; lvl++) {
|
|
516
|
+
const iso = lvl / levels;
|
|
517
|
+
const [h, sat, bri] = palette[lvl % palette.length];
|
|
518
|
+
const satBoost = map(S.treble, 0, 100, 1.0, 1.4);
|
|
519
|
+
const alpha = lerp(0.95, 0.5, iso) * map(S.volume, 0, 100, 0.5, 1.0);
|
|
520
|
+
|
|
521
|
+
fill(hslColor(h, constrain(sat * satBoost, 0, 100), bri, alpha));
|
|
522
|
+
|
|
523
|
+
const scale = 0.012 + lvl * 0.0025;
|
|
524
|
+
for (let x = 0; x < width; x += res) {
|
|
525
|
+
for (let y = 0; y < height; y += res) {
|
|
526
|
+
if (noise(x * scale, y * scale) > iso * map(S.rhythmicity, 0, 100, 3.4, 8.3)) {
|
|
527
|
+
const rw = res * (0.9 + random() * 0.3);
|
|
528
|
+
const rh = res * (0.9 + random() * 0.3);
|
|
529
|
+
rect(x, y, rw, rh);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function hslColor(h, s, l, a) {
|
|
537
|
+
if (a !== undefined) return 'hsla(' + h + ',' + s + '%,' + l + '%,' + a + ')';
|
|
538
|
+
return 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
|
539
|
+
}
|
|
540
|
+
`;
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
// soundart-sketches/prismFlowFields.ts
|
|
545
|
+
var PRISM_FLOW_FIELDS_SKETCH;
|
|
546
|
+
var init_prismFlowFields = __esm({
|
|
547
|
+
"soundart-sketches/prismFlowFields.ts"() {
|
|
548
|
+
PRISM_FLOW_FIELDS_SKETCH = `
|
|
549
|
+
function setup() {
|
|
550
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
551
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
552
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
553
|
+
else {
|
|
554
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
555
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
556
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
557
|
+
background(r, g, b);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
const particleCount = Math.floor(map(S.volume, 0, 100, 1500, 8200));
|
|
561
|
+
const maxSteps = Math.floor(map(S.rhythmicity, 0, 100, 200, 680));
|
|
562
|
+
const weightMin = map(S.volume, 0, 100, 0.5, 1.8);
|
|
563
|
+
const weightMax = map(S.treble, 0, 100, 1.2, 3.2);
|
|
564
|
+
const angleMult = map(max(0, S.harmonicity - 5), 0, 95, 2.5, 7.0);
|
|
565
|
+
const stepJitter = map(S.aggression, 0, 100, 0.5, 2.5);
|
|
566
|
+
const skewOffset = map(S.hue, 0, 100, -PI / 6, PI / 6);
|
|
567
|
+
const alphaBase = map(S.brightness, 0, 100, 0.4, 0.7);
|
|
568
|
+
const scaleF = map(S.harmonicity, 0, 100, 0.0001, 0.0024);
|
|
569
|
+
|
|
570
|
+
noFill();
|
|
571
|
+
|
|
572
|
+
for (let i = 0; i < particleCount; i++) {
|
|
573
|
+
let x = random() * width;
|
|
574
|
+
let y = random() * height;
|
|
575
|
+
const hueShift = (i / particleCount) * 360 + (S.hue * 3.6);
|
|
576
|
+
const h = hueShift % 360;
|
|
577
|
+
const sat = map(S.aggression, 0, 100, 70, 100);
|
|
578
|
+
const bri = map(S.brightness, 0, 100, 60, 100);
|
|
579
|
+
|
|
580
|
+
stroke('hsla(' + h + ',' + sat + '%,' + bri + '%,' + alphaBase + ')');
|
|
581
|
+
strokeWeight(map(random(), 0, 1, weightMin, weightMax));
|
|
582
|
+
|
|
583
|
+
beginShape();
|
|
584
|
+
vertex(x, y);
|
|
585
|
+
for (let j = 0; j < maxSteps; j++) {
|
|
586
|
+
const angle = noise(x * scaleF, y * scaleF) * PI * 4 + skewOffset;
|
|
587
|
+
const dx = cos(angle) * (angleMult + (random() - 0.5) * stepJitter);
|
|
588
|
+
const dy = sin(angle) * (angleMult + (random() - 0.5) * stepJitter);
|
|
589
|
+
x += dx;
|
|
590
|
+
y += dy;
|
|
591
|
+
if (x < 0 || x > width || y < 0 || y > height) break;
|
|
592
|
+
vertex(x, y);
|
|
593
|
+
}
|
|
594
|
+
endShape();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
`;
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// soundart-sketches/chladniBloom.ts
|
|
602
|
+
var CHLADNI_BLOOM_SKETCH;
|
|
603
|
+
var init_chladniBloom = __esm({
|
|
604
|
+
"soundart-sketches/chladniBloom.ts"() {
|
|
605
|
+
CHLADNI_BLOOM_SKETCH = `
|
|
606
|
+
function setup() {
|
|
607
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
608
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
609
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
610
|
+
else {
|
|
611
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
612
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
613
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
614
|
+
background(r, g, b);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
translate(width / 2, height / 2);
|
|
618
|
+
|
|
619
|
+
const layers = Math.floor(map(S.volume + S.rhythmicity, 0, 200, 20, 90));
|
|
620
|
+
const minStrokeW = map(S.volume, 0, 100, 1.0, 3.0);
|
|
621
|
+
const maxStrokeW = map(S.treble, 0, 100, 2.0, 5.0);
|
|
622
|
+
|
|
623
|
+
noFill();
|
|
624
|
+
|
|
625
|
+
for (let i = 0; i < layers; i++) {
|
|
626
|
+
const freq1 = Math.floor(map(S.aggression + random() * 50, 0, 150, 3, 20));
|
|
627
|
+
const freq2 = Math.floor(map(S.treble + random() * 50, 0, 150, 2, 8));
|
|
628
|
+
const amp = map(S.dynamicRange + random() * 30, 0, 130, 60, 400);
|
|
629
|
+
const rotation = map(S.harmonicity + random() * 50, 0, 150, 0, PI * 4);
|
|
630
|
+
const radiusOffset = map(S.bass, 0, 100, 40, 80);
|
|
631
|
+
const strokeW = map(S.treble + S.volume * 0.5, 0, 100, minStrokeW, maxStrokeW);
|
|
632
|
+
|
|
633
|
+
const hBase = (330 + random() * 100) % 360;
|
|
634
|
+
const finalH = (hBase + (random() * 40 - 60) + map(S.hue, 0, 100, -10, 10) + 360) % 360;
|
|
635
|
+
const finalC = constrain(85 + map(S.brightness, 0, 100, -20, 20), 0, 100);
|
|
636
|
+
const finalL = constrain(90 + map(S.brightness, 0, 100, -15, 15), 0, 100);
|
|
637
|
+
const finalA = constrain(0.5 + map(S.attack, 0, 100, 0, 0.5), 0.3, 1);
|
|
638
|
+
|
|
639
|
+
push();
|
|
640
|
+
rotate(rotation);
|
|
641
|
+
strokeWeight(strokeW);
|
|
642
|
+
stroke('hsla(' + finalH + ',' + finalC + '%,' + finalL + '%,' + finalA + ')');
|
|
643
|
+
|
|
644
|
+
const angleStep = map(S.treble, 0, 100, 0.07, 0.03);
|
|
645
|
+
beginShape();
|
|
646
|
+
for (let aa = 0; aa < PI * 2; aa += angleStep) {
|
|
647
|
+
const rad = radiusOffset * i + amp * sin(freq1 * aa) * cos(freq2 * aa * map(S.rhythmicity, 0, 100, 0.3, 2));
|
|
648
|
+
const x = rad * cos(aa);
|
|
649
|
+
const y = rad * sin(aa);
|
|
650
|
+
vertex(x, y);
|
|
651
|
+
}
|
|
652
|
+
endShape(CLOSE);
|
|
653
|
+
pop();
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
`;
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
// soundart-sketches/dualVortex.ts
|
|
661
|
+
var DUAL_VORTEX_SKETCH;
|
|
662
|
+
var init_dualVortex = __esm({
|
|
663
|
+
"soundart-sketches/dualVortex.ts"() {
|
|
664
|
+
DUAL_VORTEX_SKETCH = `
|
|
665
|
+
function setup() {
|
|
666
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
667
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
668
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
669
|
+
else {
|
|
670
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
671
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
672
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
673
|
+
background(r, g, b);
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
const presence = 1.0 - S.silence / 100;
|
|
677
|
+
const sizeFactor = (S.length * 0.7 + S.volume * 0.6);
|
|
678
|
+
const layers = Math.floor(map(S.length, 0, 100, 60, 180));
|
|
679
|
+
const baseR = map(sizeFactor, 0, 100, Math.min(width, height) * 0.15, Math.min(width, height) * 0.55);
|
|
680
|
+
const steps = Math.floor(map(S.attack, 0, 100, 180, 480));
|
|
681
|
+
const twist = map(S.harmonicity, 0, 100, 0.5, 3.5);
|
|
682
|
+
const bulge = map(S.aggression, 0, 100, 0.05, 0.55);
|
|
683
|
+
const waveF = map(S.rhythmicity, 0, 100, 0.8, 5.0);
|
|
684
|
+
const CY = height * 0.5 + map(S.bass, 0, 100, -height * 0.1, height * 0.1);
|
|
685
|
+
const wBase = map(S.volume, 0, 100, 1.5, 6.0);
|
|
686
|
+
const alphaLo = map(S.dynamicRange, 0, 100, 80, 180) * presence;
|
|
687
|
+
const alphaHi = map(S.dynamicRange, 0, 100, 180, 255) * presence;
|
|
688
|
+
|
|
689
|
+
const mainHue = (S.brightness * 3.6) % 360;
|
|
690
|
+
|
|
691
|
+
noFill();
|
|
692
|
+
|
|
693
|
+
for (let pass = 0; pass < 2; pass++) {
|
|
694
|
+
const isSecond = pass === 0;
|
|
695
|
+
const alphaScale = isSecond ? 0.7 : 1.0;
|
|
696
|
+
const weightScale = isSecond ? 0.85 : 1.1;
|
|
697
|
+
|
|
698
|
+
for (let i = 0; i < layers; i++) {
|
|
699
|
+
const t = i / (layers - 1);
|
|
700
|
+
const h = (mainHue + i * 22.5) % 360;
|
|
701
|
+
const sat = map(S.treble, 0, 100, 60, 90);
|
|
702
|
+
const bri = map(S.bass, 0, 100, 70, 95);
|
|
703
|
+
|
|
704
|
+
const r0 = baseR * (0.6 + (isSecond ? 0.8 : 0.7) * sin(PI * 2 * t + 0.42));
|
|
705
|
+
const rot = t * (twist + 0.6) + sin(i * 0.05) * map(S.attack, 0, 100, 0.02, 0.12);
|
|
706
|
+
|
|
707
|
+
const hueShift = (i / layers) * 120;
|
|
708
|
+
const finalH = (h + hueShift) % 360;
|
|
709
|
+
const a = lerp(alphaHi, alphaLo, pow(t, 0.9)) * alphaScale / 255;
|
|
710
|
+
|
|
711
|
+
push();
|
|
712
|
+
translate(width * 0.5, CY);
|
|
713
|
+
rotate(rot);
|
|
714
|
+
|
|
715
|
+
strokeWeight(max(0.6, wBase * weightScale));
|
|
716
|
+
stroke('hsla(' + finalH + ',' + sat + '%,' + bri + '%,' + constrain(a, 0, 1) + ')');
|
|
717
|
+
|
|
718
|
+
beginShape();
|
|
719
|
+
for (let k = 0; k <= steps; k++) {
|
|
720
|
+
const ang = map(k, 0, steps, 0, PI * 2);
|
|
721
|
+
const baseRR = r0 * (1.0 + bulge * sin(ang * waveF + t * PI * 3));
|
|
722
|
+
vertex(baseRR * cos(ang), baseRR * sin(ang));
|
|
723
|
+
}
|
|
724
|
+
endShape(CLOSE);
|
|
725
|
+
pop();
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
`;
|
|
730
|
+
}
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
// soundart-sketches/isoflow.ts
|
|
734
|
+
var ISOFLOW_SKETCH;
|
|
735
|
+
var init_isoflow = __esm({
|
|
736
|
+
"soundart-sketches/isoflow.ts"() {
|
|
737
|
+
ISOFLOW_SKETCH = `
|
|
738
|
+
function setup() {
|
|
739
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
740
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
741
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
742
|
+
else {
|
|
743
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
744
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
745
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
746
|
+
background(r, g, b);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
const cx = width / 2;
|
|
750
|
+
const cy = height / 2;
|
|
751
|
+
const maxRadius = min(width, height) * 0.6;
|
|
752
|
+
const layers = 3;
|
|
753
|
+
const burstsPerLayer = Math.floor(map(S.rhythmicity, 0, 100, 20, 40));
|
|
754
|
+
|
|
755
|
+
noFill();
|
|
756
|
+
|
|
757
|
+
for (let l = 0; l < layers; l++) {
|
|
758
|
+
const hue = map(l === 0 ? S.bass : (l === 1 ? S.brightness : S.treble), 0, 100, 0, 360);
|
|
759
|
+
const alpha = map(l === 0 ? S.volume : (l === 1 ? S.aggression : S.dynamicRange), 0, 100, 0.7, 1);
|
|
760
|
+
const sat = 85 + random() * 15;
|
|
761
|
+
|
|
762
|
+
stroke('hsla(' + hue + ',' + sat + '%,95%,' + alpha + ')');
|
|
763
|
+
strokeWeight(map(S.volume + l * 10, 0, 130, 1.2, 4.8));
|
|
764
|
+
|
|
765
|
+
const radiusScale = map(S.dynamicRange + l * 20, 0, 150, 0.8, 1.4);
|
|
766
|
+
const noiseAmp = map(S.aggression, 0, 100, 0.5, 3.0);
|
|
767
|
+
const swirl = map(S.treble + l * 20, 0, 150, 0.3, 1.5);
|
|
768
|
+
const rotSkew = map(S.harmonicity, 0, 100, -0.5, 0.5);
|
|
769
|
+
const deformSkew = map(S.attack, 0, 100, -PI * 0.4, PI * 0.4);
|
|
770
|
+
const centerJitter = map(S.bass, 0, 100, -60, 60);
|
|
771
|
+
|
|
772
|
+
for (let b = 0; b < burstsPerLayer; b++) {
|
|
773
|
+
const baseAngle = map(b + l * burstsPerLayer, 0, layers * burstsPerLayer, 0, PI * 2);
|
|
774
|
+
const burstCount = Math.floor(map(S.volume + S.rhythmicity, 0, 200, 30, 90));
|
|
775
|
+
|
|
776
|
+
for (let j = 0; j < burstCount; j++) {
|
|
777
|
+
const angle = baseAngle + map(j, 0, burstCount, -0.02, 0.02);
|
|
778
|
+
const ox = cx + cos(angle) * centerJitter;
|
|
779
|
+
const oy = cy + sin(angle) * centerJitter;
|
|
780
|
+
|
|
781
|
+
beginShape();
|
|
782
|
+
for (let r0 = 0; r0 < maxRadius * radiusScale; r0 += 3) {
|
|
783
|
+
const normR = r0 / maxRadius;
|
|
784
|
+
const n = noise(cos(angle + rotSkew) * normR * noiseAmp, sin(angle + swirl) * normR * noiseAmp);
|
|
785
|
+
const deform = map(n, 0, 1, -PI * swirl, PI * swirl) + deformSkew;
|
|
786
|
+
const px = ox + cos(angle + deform) * r0;
|
|
787
|
+
const py = oy + sin(angle + deform) * r0;
|
|
788
|
+
vertex(px, py);
|
|
789
|
+
}
|
|
790
|
+
endShape();
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
`;
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
// soundart-sketches/geometryIllusion.ts
|
|
800
|
+
var GEOMETRY_ILLUSION_SKETCH;
|
|
801
|
+
var init_geometryIllusion = __esm({
|
|
802
|
+
"soundart-sketches/geometryIllusion.ts"() {
|
|
803
|
+
GEOMETRY_ILLUSION_SKETCH = `
|
|
804
|
+
function setup() {
|
|
805
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
806
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
807
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
808
|
+
else {
|
|
809
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
810
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
811
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
812
|
+
background(r, g, b);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const hueBase = map(S.hue, 0, 100, 0, 360);
|
|
816
|
+
const hueVar = map(S.treble, 0, 100, 20, 240);
|
|
817
|
+
const sizeScale = map(S.volume, 0, 100, 0.05, 1.8);
|
|
818
|
+
const rotAmt = map(S.aggression, 0, 100, 0, PI / 3);
|
|
819
|
+
const layers = Math.floor(map(S.length, 0, 100, 120, 900));
|
|
820
|
+
const alphaLevel = map(S.amplitude, 0, 100, 0.25, 1.3);
|
|
821
|
+
const lineCount = Math.floor(map(S.rhythmicity, 0, 100, 40, 600));
|
|
822
|
+
const strokeW = map(S.attack, 0, 100, 0.5, 6.5);
|
|
823
|
+
const distort = map(S.harmonicity, 0, 100, 0, 70);
|
|
824
|
+
const bias = map(S.mid, 0, 100, 0, 1);
|
|
825
|
+
const bright = map(S.brightness, 0, 100, 0.4, 2.2);
|
|
826
|
+
const sat = map(S.chroma, 0, 100, 0.4, 5.4);
|
|
827
|
+
const cont = map(S.dynamicRange, 0, 100, 0.5, 3.4);
|
|
828
|
+
const layerDepth = map(S.harmonicity, 0, 100, 0.2, 2.2);
|
|
829
|
+
|
|
830
|
+
const baseScale = map(S.volume, 0, 100, 0.5, 1.1);
|
|
831
|
+
const densityScale = map(S.length, 0, 100, 0.9, 0.7);
|
|
832
|
+
const scaleFactor = constrain(baseScale * densityScale, 0.6, 1.0);
|
|
833
|
+
|
|
834
|
+
translate(width / 2, height / 2);
|
|
835
|
+
scale(scaleFactor, scaleFactor);
|
|
836
|
+
|
|
837
|
+
const combined = S.rhythmicity * 0.6 + S.harmonicity * 0.4;
|
|
838
|
+
const spacingFactor = map(combined, 0, 100, 2.5, 0.3);
|
|
839
|
+
const spacingAmp = map(S.volume, 0, 100, 0.3, 1.2);
|
|
840
|
+
|
|
841
|
+
for (let i = 0; i < layers; i++) {
|
|
842
|
+
push();
|
|
843
|
+
rotate((random() - 0.5) * rotAmt * 2);
|
|
844
|
+
|
|
845
|
+
const w = random() * width * sizeScale;
|
|
846
|
+
const h = random() * height * sizeScale;
|
|
847
|
+
|
|
848
|
+
translate(
|
|
849
|
+
(random() - 0.5) * width * spacingFactor * spacingAmp,
|
|
850
|
+
(random() - 0.5) * height * spacingFactor * spacingAmp
|
|
851
|
+
);
|
|
852
|
+
|
|
853
|
+
const hue = (hueBase + random() * hueVar - hueVar / 2 + 360) % 360;
|
|
854
|
+
const fillSat = constrain(60 * sat, 0, 100);
|
|
855
|
+
const fillLight = constrain(60 * bright * cont, 0, 100);
|
|
856
|
+
const strokeLight = constrain(30 * cont, 0, 100);
|
|
857
|
+
|
|
858
|
+
fill(hslColor(hue, fillSat, fillLight, alphaLevel * layerDepth));
|
|
859
|
+
stroke(hslColor(hue, fillSat * 0.6, strokeLight, alphaLevel));
|
|
860
|
+
strokeWeight(strokeW);
|
|
861
|
+
|
|
862
|
+
const rectW = w * (bias > 0.5 ? lerp(0.5, 1.0, random()) : lerp(0.1, 0.5, random()));
|
|
863
|
+
const rectH = h * (bias < 0.5 ? lerp(0.5, 1.0, random()) : lerp(0.1, 0.5, random()));
|
|
864
|
+
|
|
865
|
+
rect(
|
|
866
|
+
-rectW / 2 + (random() - 0.5) * distort,
|
|
867
|
+
-rectH / 2 + (random() - 0.5) * distort,
|
|
868
|
+
rectW,
|
|
869
|
+
rectH
|
|
870
|
+
);
|
|
871
|
+
pop();
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
noFill();
|
|
875
|
+
stroke(hslColor(hueBase, 25, 75, 0.25));
|
|
876
|
+
strokeWeight(1);
|
|
877
|
+
for (let i = 0; i < lineCount; i++) {
|
|
878
|
+
const x1 = random() * width - width / 2;
|
|
879
|
+
const y1 = random() * height - height / 2;
|
|
880
|
+
const x2 = x1 + lerp(-100, 100, random());
|
|
881
|
+
const y2 = y1 + lerp(-100, 100, random());
|
|
882
|
+
line(x1, y1, x2, y2);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function hslColor(h, s, l, a) {
|
|
887
|
+
if (a !== undefined) return 'hsla(' + h + ',' + s + '%,' + l + '%,' + a + ')';
|
|
888
|
+
return 'hsl(' + h + ',' + s + '%,' + l + '%)';
|
|
889
|
+
}
|
|
890
|
+
`;
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// soundart-sketches/resonantSoundBodies.ts
|
|
895
|
+
var RESONANT_SOUND_BODIES_SKETCH;
|
|
896
|
+
var init_resonantSoundBodies = __esm({
|
|
897
|
+
"soundart-sketches/resonantSoundBodies.ts"() {
|
|
898
|
+
RESONANT_SOUND_BODIES_SKETCH = `
|
|
899
|
+
function setup() {
|
|
900
|
+
const bgMode = typeof backgroundMode !== 'undefined' ? backgroundMode : 'rgb';
|
|
901
|
+
if (bgMode === 'white') background(245, 245, 245);
|
|
902
|
+
else if (bgMode === 'black') background(10, 10, 10);
|
|
903
|
+
else {
|
|
904
|
+
const r = Math.floor((S.brightness / 100) * 255);
|
|
905
|
+
const g = Math.floor((S.rhythmicity / 100) * 255);
|
|
906
|
+
const b = Math.floor((S.harmonicity / 100) * 255);
|
|
907
|
+
background(r, g, b);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
const cx = width / 2;
|
|
911
|
+
const cy = height / 2;
|
|
912
|
+
const maxR = min(width, height) * 0.45;
|
|
913
|
+
const shapeSeed = S.bass * 17.3 + S.treble * 23.7 + S.rhythmicity * 11.1;
|
|
914
|
+
|
|
915
|
+
const numBodies = Math.floor(8 + (S.volume / 100) * 12 + (S.bass / 100) * 8);
|
|
916
|
+
|
|
917
|
+
noFill();
|
|
918
|
+
|
|
919
|
+
for (let i = 0; i < numBodies; i++) {
|
|
920
|
+
const t = i / numBodies;
|
|
921
|
+
const radius = maxR * (0.15 + t * 0.85);
|
|
922
|
+
|
|
923
|
+
const hue = (((S.hue / 100) + t * 0.3 + (S.harmonicity / 100) * 0.2) % 1) * 360;
|
|
924
|
+
const sat = lerp(50, 80, S.brightness / 100);
|
|
925
|
+
const lum = lerp(30, 60, S.brightness / 100);
|
|
926
|
+
|
|
927
|
+
const alpha = lerp(0.4, 0.15, t) * (0.5 + (S.volume / 100) * 0.5);
|
|
928
|
+
stroke('hsla(' + hue + ',' + sat + '%,' + lum + '%,' + alpha + ')');
|
|
929
|
+
strokeWeight(lerp(3, 1, t) * (1 + (S.aggression / 100) * 2));
|
|
930
|
+
|
|
931
|
+
beginShape();
|
|
932
|
+
const points = Math.floor(64 + (S.aggression / 100) * 64);
|
|
933
|
+
|
|
934
|
+
for (let j = 0; j <= points; j++) {
|
|
935
|
+
const angle = (j / points) * PI * 2;
|
|
936
|
+
|
|
937
|
+
const wobble = noise(
|
|
938
|
+
cos(angle) * 3 + i * 0.5 + shapeSeed * 0.001,
|
|
939
|
+
sin(angle) * 3 + i * 0.5
|
|
940
|
+
);
|
|
941
|
+
|
|
942
|
+
const bassWobble = sin(angle * (2 + Math.floor((S.bass / 100) * 6))) * (S.bass / 100) * 0.15;
|
|
943
|
+
const trebleWobble = sin(angle * (8 + Math.floor((S.treble / 100) * 12))) * (S.treble / 100) * 0.08;
|
|
944
|
+
|
|
945
|
+
const r = radius * (1 + (wobble - 0.5) * 0.3 * (S.aggression / 100) + bassWobble + trebleWobble);
|
|
946
|
+
|
|
947
|
+
const x = cx + cos(angle) * r;
|
|
948
|
+
const y = cy + sin(angle) * r;
|
|
949
|
+
|
|
950
|
+
vertex(x, y);
|
|
951
|
+
}
|
|
952
|
+
endShape(CLOSE);
|
|
953
|
+
|
|
954
|
+
if (i % 3 === 0) {
|
|
955
|
+
const fillAlpha = alpha * 0.1;
|
|
956
|
+
fill('hsla(' + hue + ',' + sat + '%,' + lum + '%,' + fillAlpha + ')');
|
|
957
|
+
beginShape();
|
|
958
|
+
for (let j = 0; j <= points; j++) {
|
|
959
|
+
const angle = (j / points) * PI * 2;
|
|
960
|
+
const wobble = noise(cos(angle) * 3 + i * 0.5 + shapeSeed * 0.001, sin(angle) * 3 + i * 0.5);
|
|
961
|
+
const bodyR = radius * (1 + (wobble - 0.5) * 0.3 * (S.aggression / 100));
|
|
962
|
+
vertex(cx + cos(angle) * bodyR, cy + sin(angle) * bodyR);
|
|
963
|
+
}
|
|
964
|
+
endShape(CLOSE);
|
|
965
|
+
noFill();
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
const numNodes = Math.floor(12 + (S.rhythmicity / 100) * 20);
|
|
970
|
+
for (let i = 0; i < numNodes; i++) {
|
|
971
|
+
const angle = (i / numNodes) * PI * 2 + shapeSeed * 0.001;
|
|
972
|
+
const dist = maxR * (0.3 + noise(i * 0.1, shapeSeed * 0.001) * 0.6);
|
|
973
|
+
|
|
974
|
+
const x = cx + cos(angle) * dist;
|
|
975
|
+
const y = cy + sin(angle) * dist;
|
|
976
|
+
|
|
977
|
+
const nodeSize = lerp(2, 8, S.volume / 100) * (1 + (S.bass / 100) * 2);
|
|
978
|
+
const hue = ((((S.hue / 100) + i * 0.05 + (S.treble / 100) * 0.3) % 1)) * 360;
|
|
979
|
+
|
|
980
|
+
fill('hsla(' + hue + ',70%,' + lerp(40, 70, S.brightness / 100) + '%,' + lerp(0.3, 0.7, S.harmonicity / 100) + ')');
|
|
981
|
+
noStroke();
|
|
982
|
+
ellipse(x, y, nodeSize * 2, nodeSize * 2);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
`;
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
// soundart-sketches/index.ts
|
|
990
|
+
var soundart_sketches_exports = {};
|
|
991
|
+
__export(soundart_sketches_exports, {
|
|
992
|
+
CHLADNI_BLOOM_SKETCH: () => CHLADNI_BLOOM_SKETCH,
|
|
993
|
+
DUAL_VORTEX_SKETCH: () => DUAL_VORTEX_SKETCH,
|
|
994
|
+
GEOMETRY_ILLUSION_SKETCH: () => GEOMETRY_ILLUSION_SKETCH,
|
|
995
|
+
ISOFLOW_SKETCH: () => ISOFLOW_SKETCH,
|
|
996
|
+
LOOM_WEAVE_SKETCH: () => LOOM_WEAVE_SKETCH,
|
|
997
|
+
NOISE_TERRACES_SKETCH: () => NOISE_TERRACES_SKETCH,
|
|
998
|
+
ORB_SKETCH: () => ORB_SKETCH,
|
|
999
|
+
PIXEL_GLYPHS_SKETCH: () => PIXEL_GLYPHS_SKETCH,
|
|
1000
|
+
PRISM_FLOW_FIELDS_SKETCH: () => PRISM_FLOW_FIELDS_SKETCH,
|
|
1001
|
+
RADIAL_BURST_SKETCH: () => RADIAL_BURST_SKETCH,
|
|
1002
|
+
RESONANT_SOUND_BODIES_SKETCH: () => RESONANT_SOUND_BODIES_SKETCH,
|
|
1003
|
+
RINGS_SKETCH: () => RINGS_SKETCH,
|
|
1004
|
+
SOUNDART_SKETCHES: () => SOUNDART_SKETCHES,
|
|
1005
|
+
SQUARES_SKETCH: () => SQUARES_SKETCH,
|
|
1006
|
+
WAVE_STRIPES_SKETCH: () => WAVE_STRIPES_SKETCH,
|
|
1007
|
+
getAvailableSoundArtSketches: () => getAvailableSoundArtSketches,
|
|
1008
|
+
getSoundArtSketch: () => getSoundArtSketch,
|
|
1009
|
+
isSoundArtSketchAvailable: () => isSoundArtSketchAvailable
|
|
1010
|
+
});
|
|
1011
|
+
function getSoundArtSketch(name) {
|
|
1012
|
+
return SOUNDART_SKETCHES[name];
|
|
1013
|
+
}
|
|
1014
|
+
function getAvailableSoundArtSketches() {
|
|
1015
|
+
return Object.keys(SOUNDART_SKETCHES);
|
|
1016
|
+
}
|
|
1017
|
+
function isSoundArtSketchAvailable(name) {
|
|
1018
|
+
return name in SOUNDART_SKETCHES;
|
|
1019
|
+
}
|
|
1020
|
+
var SOUNDART_SKETCHES;
|
|
1021
|
+
var init_soundart_sketches = __esm({
|
|
1022
|
+
"soundart-sketches/index.ts"() {
|
|
1023
|
+
init_rings();
|
|
1024
|
+
init_pixelGlyphs();
|
|
1025
|
+
init_radialBurst();
|
|
1026
|
+
init_orb();
|
|
1027
|
+
init_waveStripes();
|
|
1028
|
+
init_squares();
|
|
1029
|
+
init_loomWeave();
|
|
1030
|
+
init_noiseTerraces();
|
|
1031
|
+
init_prismFlowFields();
|
|
1032
|
+
init_chladniBloom();
|
|
1033
|
+
init_dualVortex();
|
|
1034
|
+
init_isoflow();
|
|
1035
|
+
init_geometryIllusion();
|
|
1036
|
+
init_resonantSoundBodies();
|
|
1037
|
+
init_rings();
|
|
1038
|
+
init_pixelGlyphs();
|
|
1039
|
+
init_radialBurst();
|
|
1040
|
+
init_orb();
|
|
1041
|
+
init_waveStripes();
|
|
1042
|
+
init_squares();
|
|
1043
|
+
init_loomWeave();
|
|
1044
|
+
init_noiseTerraces();
|
|
1045
|
+
init_prismFlowFields();
|
|
1046
|
+
init_chladniBloom();
|
|
1047
|
+
init_dualVortex();
|
|
1048
|
+
init_isoflow();
|
|
1049
|
+
init_geometryIllusion();
|
|
1050
|
+
init_resonantSoundBodies();
|
|
1051
|
+
SOUNDART_SKETCHES = {
|
|
1052
|
+
rings: RINGS_SKETCH,
|
|
1053
|
+
pixelGlyphs: PIXEL_GLYPHS_SKETCH,
|
|
1054
|
+
radialBurst: RADIAL_BURST_SKETCH,
|
|
1055
|
+
orb: ORB_SKETCH,
|
|
1056
|
+
waveStripes: WAVE_STRIPES_SKETCH,
|
|
1057
|
+
squares: SQUARES_SKETCH,
|
|
1058
|
+
loomWeave: LOOM_WEAVE_SKETCH,
|
|
1059
|
+
noiseTerraces: NOISE_TERRACES_SKETCH,
|
|
1060
|
+
prismFlowFields: PRISM_FLOW_FIELDS_SKETCH,
|
|
1061
|
+
chladniBloom: CHLADNI_BLOOM_SKETCH,
|
|
1062
|
+
dualVortex: DUAL_VORTEX_SKETCH,
|
|
1063
|
+
isoflow: ISOFLOW_SKETCH,
|
|
1064
|
+
geometryIllusion: GEOMETRY_ILLUSION_SKETCH,
|
|
1065
|
+
resonantSoundBodies: RESONANT_SOUND_BODIES_SKETCH
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
|
|
1070
|
+
// version.ts
|
|
1071
|
+
var SDK_VERSION = "1.8.4";
|
|
1072
|
+
var PROTOCOL_VERSION = "1.2.0";
|
|
1073
|
+
var PROTOCOL_PHASE = 3;
|
|
1074
|
+
|
|
1075
|
+
// types.ts
|
|
1076
|
+
var PROTOCOL_IDENTITY = {
|
|
1077
|
+
protocol: "nexart",
|
|
1078
|
+
engine: "codemode",
|
|
1079
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
1080
|
+
phase: PROTOCOL_PHASE,
|
|
1081
|
+
deterministic: true
|
|
1082
|
+
};
|
|
1083
|
+
var DEFAULT_VARS = {
|
|
1084
|
+
VAR: Object.freeze([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
1085
|
+
};
|
|
1086
|
+
var DEFAULT_CONFIG = {
|
|
1087
|
+
width: 1950,
|
|
1088
|
+
height: 2400,
|
|
1089
|
+
duration: 2,
|
|
1090
|
+
fps: 30,
|
|
1091
|
+
minDuration: 1,
|
|
1092
|
+
maxDuration: 4
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
// execution-sandbox.ts
|
|
1096
|
+
function createForbiddenStub(name) {
|
|
1097
|
+
const stub = function() {
|
|
1098
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
|
|
1099
|
+
};
|
|
1100
|
+
return new Proxy(stub, {
|
|
1101
|
+
get(_target, prop) {
|
|
1102
|
+
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
1103
|
+
return () => {
|
|
1104
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1107
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}.${String(prop)}`);
|
|
1108
|
+
},
|
|
1109
|
+
apply() {
|
|
1110
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}()`);
|
|
1111
|
+
},
|
|
1112
|
+
construct() {
|
|
1113
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: new ${name}()`);
|
|
1114
|
+
},
|
|
1115
|
+
set() {
|
|
1116
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name} (assignment blocked)`);
|
|
1117
|
+
},
|
|
1118
|
+
has() {
|
|
1119
|
+
return true;
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
function createForbiddenObject(name) {
|
|
1124
|
+
return new Proxy({}, {
|
|
1125
|
+
get(_target, prop) {
|
|
1126
|
+
if (prop === Symbol.toPrimitive || prop === "toString" || prop === "valueOf") {
|
|
1127
|
+
return () => {
|
|
1128
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}`);
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}.${String(prop)}`);
|
|
1132
|
+
},
|
|
1133
|
+
set() {
|
|
1134
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name} (assignment blocked)`);
|
|
1135
|
+
},
|
|
1136
|
+
has() {
|
|
1137
|
+
return true;
|
|
1138
|
+
},
|
|
1139
|
+
apply() {
|
|
1140
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: ${name}()`);
|
|
1141
|
+
},
|
|
1142
|
+
construct() {
|
|
1143
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden API: new ${name}()`);
|
|
1144
|
+
}
|
|
1145
|
+
});
|
|
1146
|
+
}
|
|
1147
|
+
var FORBIDDEN_APIS = {
|
|
1148
|
+
Date: createForbiddenStub("Date"),
|
|
1149
|
+
performance: createForbiddenObject("performance"),
|
|
1150
|
+
process: createForbiddenObject("process"),
|
|
1151
|
+
navigator: createForbiddenObject("navigator"),
|
|
1152
|
+
globalThis: createForbiddenObject("globalThis"),
|
|
1153
|
+
crypto: createForbiddenObject("crypto"),
|
|
1154
|
+
setTimeout: createForbiddenStub("setTimeout"),
|
|
1155
|
+
setInterval: createForbiddenStub("setInterval"),
|
|
1156
|
+
clearTimeout: createForbiddenStub("clearTimeout"),
|
|
1157
|
+
clearInterval: createForbiddenStub("clearInterval"),
|
|
1158
|
+
requestAnimationFrame: createForbiddenStub("requestAnimationFrame"),
|
|
1159
|
+
cancelAnimationFrame: createForbiddenStub("cancelAnimationFrame"),
|
|
1160
|
+
fetch: createForbiddenStub("fetch"),
|
|
1161
|
+
XMLHttpRequest: createForbiddenStub("XMLHttpRequest"),
|
|
1162
|
+
WebSocket: createForbiddenStub("WebSocket"),
|
|
1163
|
+
document: createForbiddenObject("document"),
|
|
1164
|
+
window: createForbiddenObject("window"),
|
|
1165
|
+
self: createForbiddenObject("self"),
|
|
1166
|
+
top: createForbiddenObject("top"),
|
|
1167
|
+
parent: createForbiddenObject("parent"),
|
|
1168
|
+
frames: createForbiddenObject("frames"),
|
|
1169
|
+
location: createForbiddenObject("location"),
|
|
1170
|
+
history: createForbiddenObject("history"),
|
|
1171
|
+
localStorage: createForbiddenObject("localStorage"),
|
|
1172
|
+
sessionStorage: createForbiddenObject("sessionStorage"),
|
|
1173
|
+
indexedDB: createForbiddenObject("indexedDB"),
|
|
1174
|
+
caches: createForbiddenObject("caches"),
|
|
1175
|
+
Notification: createForbiddenStub("Notification"),
|
|
1176
|
+
Worker: createForbiddenStub("Worker"),
|
|
1177
|
+
SharedWorker: createForbiddenStub("SharedWorker"),
|
|
1178
|
+
ServiceWorker: createForbiddenObject("ServiceWorker"),
|
|
1179
|
+
Blob: createForbiddenStub("Blob"),
|
|
1180
|
+
File: createForbiddenStub("File"),
|
|
1181
|
+
FileReader: createForbiddenStub("FileReader"),
|
|
1182
|
+
URL: createForbiddenStub("URL"),
|
|
1183
|
+
URLSearchParams: createForbiddenStub("URLSearchParams"),
|
|
1184
|
+
Headers: createForbiddenStub("Headers"),
|
|
1185
|
+
Request: createForbiddenStub("Request"),
|
|
1186
|
+
Response: createForbiddenStub("Response"),
|
|
1187
|
+
EventSource: createForbiddenStub("EventSource"),
|
|
1188
|
+
Image: createForbiddenStub("Image"),
|
|
1189
|
+
Audio: createForbiddenStub("Audio"),
|
|
1190
|
+
Video: createForbiddenStub("Video"),
|
|
1191
|
+
eval: createForbiddenStub("eval"),
|
|
1192
|
+
Function: createForbiddenStub("Function")
|
|
1193
|
+
};
|
|
1194
|
+
function createSafeMath() {
|
|
1195
|
+
const safeMath = Object.create(Math);
|
|
1196
|
+
Object.defineProperty(safeMath, "random", {
|
|
1197
|
+
get() {
|
|
1198
|
+
throw new Error("[Code Mode Protocol Error] Forbidden API: Math.random() \u2014 use random() instead (seeded)");
|
|
1199
|
+
},
|
|
1200
|
+
configurable: false,
|
|
1201
|
+
enumerable: true
|
|
1202
|
+
});
|
|
1203
|
+
return Object.freeze(safeMath);
|
|
1204
|
+
}
|
|
1205
|
+
var FORBIDDEN_API_NAMES = Object.keys(FORBIDDEN_APIS);
|
|
1206
|
+
function buildSandboxContext(p5Runtime) {
|
|
1207
|
+
const safeMath = createSafeMath();
|
|
1208
|
+
const context = {
|
|
1209
|
+
...FORBIDDEN_APIS,
|
|
1210
|
+
Math: safeMath
|
|
1211
|
+
};
|
|
1212
|
+
for (const key of Object.keys(p5Runtime)) {
|
|
1213
|
+
context[key] = p5Runtime[key];
|
|
1214
|
+
}
|
|
1215
|
+
return context;
|
|
1216
|
+
}
|
|
1217
|
+
function createSandboxedExecutor(code, additionalParams = []) {
|
|
1218
|
+
const forbiddenParamNames = Object.keys(FORBIDDEN_APIS);
|
|
1219
|
+
const allParams = [
|
|
1220
|
+
"context",
|
|
1221
|
+
"Math",
|
|
1222
|
+
...forbiddenParamNames,
|
|
1223
|
+
...additionalParams
|
|
1224
|
+
];
|
|
1225
|
+
const wrappedCode = `
|
|
1226
|
+
"use strict";
|
|
1227
|
+
with(context) {
|
|
1228
|
+
${code}
|
|
1229
|
+
}
|
|
1230
|
+
`;
|
|
1231
|
+
return new Function(...allParams, wrappedCode);
|
|
1232
|
+
}
|
|
1233
|
+
function executeSandboxed(code, p5Runtime, additionalContext = {}) {
|
|
1234
|
+
const safeMath = createSafeMath();
|
|
1235
|
+
const fullContext = {
|
|
1236
|
+
...p5Runtime,
|
|
1237
|
+
...additionalContext,
|
|
1238
|
+
Math: safeMath,
|
|
1239
|
+
...FORBIDDEN_APIS
|
|
1240
|
+
};
|
|
1241
|
+
const forbiddenValues = Object.keys(FORBIDDEN_APIS).map((key) => FORBIDDEN_APIS[key]);
|
|
1242
|
+
const additionalParamNames = Object.keys(additionalContext);
|
|
1243
|
+
const additionalValues = Object.values(additionalContext);
|
|
1244
|
+
const executor = createSandboxedExecutor(code, additionalParamNames);
|
|
1245
|
+
executor(
|
|
1246
|
+
fullContext,
|
|
1247
|
+
safeMath,
|
|
1248
|
+
...forbiddenValues,
|
|
1249
|
+
...additionalValues
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
// p5-runtime.ts
|
|
1254
|
+
var CODE_MODE_PROTOCOL_VERSION = PROTOCOL_VERSION;
|
|
1255
|
+
var CODE_MODE_PROTOCOL_PHASE = PROTOCOL_PHASE;
|
|
1256
|
+
var CODE_MODE_ENFORCEMENT = "HARD";
|
|
1257
|
+
function createSeededRNG(seed = 123456) {
|
|
1258
|
+
let a = seed >>> 0;
|
|
1259
|
+
return () => {
|
|
1260
|
+
a += 1831565813;
|
|
1261
|
+
let t = Math.imul(a ^ a >>> 15, a | 1);
|
|
1262
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
1263
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
function createSeededNoise(seed = 0) {
|
|
1267
|
+
const permutation = [];
|
|
1268
|
+
const rng = createSeededRNG(seed);
|
|
1269
|
+
for (let i = 0; i < 256; i++) {
|
|
1270
|
+
permutation[i] = i;
|
|
1271
|
+
}
|
|
1272
|
+
for (let i = 255; i > 0; i--) {
|
|
1273
|
+
const j = Math.floor(rng() * (i + 1));
|
|
1274
|
+
[permutation[i], permutation[j]] = [permutation[j], permutation[i]];
|
|
1275
|
+
}
|
|
1276
|
+
for (let i = 0; i < 256; i++) {
|
|
1277
|
+
permutation[256 + i] = permutation[i];
|
|
1278
|
+
}
|
|
1279
|
+
const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
|
|
1280
|
+
const lerp = (a, b, t) => a + t * (b - a);
|
|
1281
|
+
const grad = (hash, x, y, z) => {
|
|
1282
|
+
const h = hash & 15;
|
|
1283
|
+
const u = h < 8 ? x : y;
|
|
1284
|
+
const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
|
|
1285
|
+
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
|
1286
|
+
};
|
|
1287
|
+
return (x, y = 0, z = 0) => {
|
|
1288
|
+
const X = Math.floor(x) & 255;
|
|
1289
|
+
const Y = Math.floor(y) & 255;
|
|
1290
|
+
const Z = Math.floor(z) & 255;
|
|
1291
|
+
x -= Math.floor(x);
|
|
1292
|
+
y -= Math.floor(y);
|
|
1293
|
+
z -= Math.floor(z);
|
|
1294
|
+
const u = fade(x);
|
|
1295
|
+
const v = fade(y);
|
|
1296
|
+
const w = fade(z);
|
|
1297
|
+
const A = permutation[X] + Y;
|
|
1298
|
+
const AA = permutation[A] + Z;
|
|
1299
|
+
const AB = permutation[A + 1] + Z;
|
|
1300
|
+
const B = permutation[X + 1] + Y;
|
|
1301
|
+
const BA = permutation[B] + Z;
|
|
1302
|
+
const BB = permutation[B + 1] + Z;
|
|
1303
|
+
return (lerp(
|
|
1304
|
+
lerp(
|
|
1305
|
+
lerp(grad(permutation[AA], x, y, z), grad(permutation[BA], x - 1, y, z), u),
|
|
1306
|
+
lerp(grad(permutation[AB], x, y - 1, z), grad(permutation[BB], x - 1, y - 1, z), u),
|
|
1307
|
+
v
|
|
1308
|
+
),
|
|
1309
|
+
lerp(
|
|
1310
|
+
lerp(grad(permutation[AA + 1], x, y, z - 1), grad(permutation[BA + 1], x - 1, y, z - 1), u),
|
|
1311
|
+
lerp(grad(permutation[AB + 1], x, y - 1, z - 1), grad(permutation[BB + 1], x - 1, y - 1, z - 1), u),
|
|
1312
|
+
v
|
|
1313
|
+
),
|
|
1314
|
+
w
|
|
1315
|
+
) + 1) / 2;
|
|
1316
|
+
};
|
|
1317
|
+
}
|
|
1318
|
+
function createP5Runtime(canvas, width, height, config) {
|
|
1319
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
1320
|
+
if (!ctx) throw new Error("Failed to get 2D context");
|
|
1321
|
+
let currentFill = "rgba(255, 255, 255, 1)";
|
|
1322
|
+
let currentStroke = "rgba(0, 0, 0, 1)";
|
|
1323
|
+
let strokeEnabled = true;
|
|
1324
|
+
let fillEnabled = true;
|
|
1325
|
+
let currentStrokeWeight = 1;
|
|
1326
|
+
let colorModeSettings = { mode: "RGB", maxR: 255, maxG: 255, maxB: 255, maxA: 255 };
|
|
1327
|
+
let shapeVertices = [];
|
|
1328
|
+
let pixelData = null;
|
|
1329
|
+
let imageDataObj = null;
|
|
1330
|
+
let currentTextSize = 12;
|
|
1331
|
+
let currentTextFont = "sans-serif";
|
|
1332
|
+
let currentTextAlignH = "left";
|
|
1333
|
+
let currentTextAlignV = "alphabetic";
|
|
1334
|
+
let randomSeedValue = config?.seed ?? Math.floor(Math.random() * 2147483647);
|
|
1335
|
+
let rng = createSeededRNG(randomSeedValue);
|
|
1336
|
+
let noiseSeedValue = config?.seed ?? 0;
|
|
1337
|
+
let noiseFunc = createSeededNoise(noiseSeedValue);
|
|
1338
|
+
let noiseOctaves = 4;
|
|
1339
|
+
let noiseFalloff = 0.5;
|
|
1340
|
+
const parseCssColor = (str) => {
|
|
1341
|
+
const s = str.trim();
|
|
1342
|
+
if (s.startsWith("#")) {
|
|
1343
|
+
const hex = s.slice(1);
|
|
1344
|
+
if (hex.length === 3) {
|
|
1345
|
+
const r = parseInt(hex[0] + hex[0], 16);
|
|
1346
|
+
const g = parseInt(hex[1] + hex[1], 16);
|
|
1347
|
+
const b = parseInt(hex[2] + hex[2], 16);
|
|
1348
|
+
return { r, g, b, a: 1 };
|
|
1349
|
+
} else if (hex.length === 6) {
|
|
1350
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1351
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1352
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1353
|
+
return { r, g, b, a: 1 };
|
|
1354
|
+
} else if (hex.length === 8) {
|
|
1355
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1356
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1357
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1358
|
+
const a = parseInt(hex.slice(6, 8), 16) / 255;
|
|
1359
|
+
return { r, g, b, a };
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
const rgbMatch = s.match(/^rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)$/i);
|
|
1363
|
+
if (rgbMatch) {
|
|
1364
|
+
return {
|
|
1365
|
+
r: parseInt(rgbMatch[1]),
|
|
1366
|
+
g: parseInt(rgbMatch[2]),
|
|
1367
|
+
b: parseInt(rgbMatch[3]),
|
|
1368
|
+
a: 1
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
const rgbaMatch = s.match(/^rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\/\s]\s*([\d.]+)\s*\)$/i);
|
|
1372
|
+
if (rgbaMatch) {
|
|
1373
|
+
return {
|
|
1374
|
+
r: parseInt(rgbaMatch[1]),
|
|
1375
|
+
g: parseInt(rgbaMatch[2]),
|
|
1376
|
+
b: parseInt(rgbaMatch[3]),
|
|
1377
|
+
a: parseFloat(rgbaMatch[4])
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const hslMatch = s.match(/^hsla?\s*\(\s*([\d.]+)\s*[,\s]\s*([\d.]+)%?\s*[,\s]\s*([\d.]+)%?\s*(?:[,\/\s]\s*([\d.]+))?\s*\)$/i);
|
|
1381
|
+
if (hslMatch) {
|
|
1382
|
+
const h = parseFloat(hslMatch[1]) / 360;
|
|
1383
|
+
const sat = parseFloat(hslMatch[2]) / 100;
|
|
1384
|
+
const l = parseFloat(hslMatch[3]) / 100;
|
|
1385
|
+
const a = hslMatch[4] ? parseFloat(hslMatch[4]) : 1;
|
|
1386
|
+
let r, g, b;
|
|
1387
|
+
if (sat === 0) {
|
|
1388
|
+
r = g = b = l;
|
|
1389
|
+
} else {
|
|
1390
|
+
const hue2rgb = (p3, q2, t) => {
|
|
1391
|
+
if (t < 0) t += 1;
|
|
1392
|
+
if (t > 1) t -= 1;
|
|
1393
|
+
if (t < 1 / 6) return p3 + (q2 - p3) * 6 * t;
|
|
1394
|
+
if (t < 1 / 2) return q2;
|
|
1395
|
+
if (t < 2 / 3) return p3 + (q2 - p3) * (2 / 3 - t) * 6;
|
|
1396
|
+
return p3;
|
|
1397
|
+
};
|
|
1398
|
+
const q = l < 0.5 ? l * (1 + sat) : l + sat - l * sat;
|
|
1399
|
+
const p2 = 2 * l - q;
|
|
1400
|
+
r = hue2rgb(p2, q, h + 1 / 3);
|
|
1401
|
+
g = hue2rgb(p2, q, h);
|
|
1402
|
+
b = hue2rgb(p2, q, h - 1 / 3);
|
|
1403
|
+
}
|
|
1404
|
+
return {
|
|
1405
|
+
r: Math.round(r * 255),
|
|
1406
|
+
g: Math.round(g * 255),
|
|
1407
|
+
b: Math.round(b * 255),
|
|
1408
|
+
a
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
return null;
|
|
1412
|
+
};
|
|
1413
|
+
const parseColor = (...args) => {
|
|
1414
|
+
if (args.length === 0) return "rgba(0, 0, 0, 1)";
|
|
1415
|
+
const { mode, maxR, maxG, maxB, maxA } = colorModeSettings;
|
|
1416
|
+
if (args.length === 1) {
|
|
1417
|
+
const val = args[0];
|
|
1418
|
+
if (typeof val === "string") {
|
|
1419
|
+
const parsed = parseCssColor(val);
|
|
1420
|
+
if (parsed) {
|
|
1421
|
+
return `rgba(${parsed.r}, ${parsed.g}, ${parsed.b}, ${parsed.a})`;
|
|
1422
|
+
}
|
|
1423
|
+
return val;
|
|
1424
|
+
}
|
|
1425
|
+
if (mode === "HSB") {
|
|
1426
|
+
return `hsla(${val}, 100%, 50%, 1)`;
|
|
1427
|
+
}
|
|
1428
|
+
const gray = Math.round(val / maxR * 255);
|
|
1429
|
+
return `rgba(${gray}, ${gray}, ${gray}, 1)`;
|
|
1430
|
+
}
|
|
1431
|
+
if (args.length === 2) {
|
|
1432
|
+
const [gray, alpha] = args;
|
|
1433
|
+
const g = Math.round(gray / maxR * 255);
|
|
1434
|
+
const a = alpha / maxA;
|
|
1435
|
+
return `rgba(${g}, ${g}, ${g}, ${a})`;
|
|
1436
|
+
}
|
|
1437
|
+
if (args.length === 3) {
|
|
1438
|
+
const [r, g, b] = args;
|
|
1439
|
+
if (mode === "HSB") {
|
|
1440
|
+
return `hsla(${r / maxR * 360}, ${g / maxG * 100}%, ${b / maxB * 100}%, 1)`;
|
|
1441
|
+
}
|
|
1442
|
+
return `rgba(${Math.round(r / maxR * 255)}, ${Math.round(g / maxG * 255)}, ${Math.round(b / maxB * 255)}, 1)`;
|
|
1443
|
+
}
|
|
1444
|
+
if (args.length === 4) {
|
|
1445
|
+
const [r, g, b, a] = args;
|
|
1446
|
+
if (mode === "HSB") {
|
|
1447
|
+
return `hsla(${r / maxR * 360}, ${g / maxG * 100}%, ${b / maxB * 100}%, ${a / maxA})`;
|
|
1448
|
+
}
|
|
1449
|
+
return `rgba(${Math.round(r / maxR * 255)}, ${Math.round(g / maxG * 255)}, ${Math.round(b / maxB * 255)}, ${a / maxA})`;
|
|
1450
|
+
}
|
|
1451
|
+
return "rgba(0, 0, 0, 1)";
|
|
1452
|
+
};
|
|
1453
|
+
const p = {
|
|
1454
|
+
width,
|
|
1455
|
+
height,
|
|
1456
|
+
frameCount: 0,
|
|
1457
|
+
// Constants
|
|
1458
|
+
PI: Math.PI,
|
|
1459
|
+
TWO_PI: Math.PI * 2,
|
|
1460
|
+
TAU: Math.PI * 2,
|
|
1461
|
+
HALF_PI: Math.PI / 2,
|
|
1462
|
+
QUARTER_PI: Math.PI / 4,
|
|
1463
|
+
// Shape mode constants
|
|
1464
|
+
CORNER: "corner",
|
|
1465
|
+
CENTER: "center",
|
|
1466
|
+
CORNERS: "corners",
|
|
1467
|
+
RADIUS: "radius",
|
|
1468
|
+
ROUND: "round",
|
|
1469
|
+
SQUARE: "butt",
|
|
1470
|
+
PROJECT: "square",
|
|
1471
|
+
MITER: "miter",
|
|
1472
|
+
BEVEL: "bevel",
|
|
1473
|
+
CLOSE: "close",
|
|
1474
|
+
PIE: "pie",
|
|
1475
|
+
CHORD: "chord",
|
|
1476
|
+
OPEN: "open",
|
|
1477
|
+
// Blend mode constants (v1.1)
|
|
1478
|
+
NORMAL: "source-over",
|
|
1479
|
+
ADD: "lighter",
|
|
1480
|
+
MULTIPLY: "multiply",
|
|
1481
|
+
SCREEN: "screen",
|
|
1482
|
+
// Text alignment constants
|
|
1483
|
+
LEFT: "left",
|
|
1484
|
+
RIGHT: "right",
|
|
1485
|
+
TOP: "top",
|
|
1486
|
+
BOTTOM: "bottom",
|
|
1487
|
+
BASELINE: "alphabetic",
|
|
1488
|
+
// Canvas operations
|
|
1489
|
+
background: (...args) => {
|
|
1490
|
+
ctx.save();
|
|
1491
|
+
ctx.fillStyle = parseColor(...args);
|
|
1492
|
+
ctx.fillRect(0, 0, width, height);
|
|
1493
|
+
ctx.restore();
|
|
1494
|
+
},
|
|
1495
|
+
clear: () => {
|
|
1496
|
+
ctx.clearRect(0, 0, width, height);
|
|
1497
|
+
},
|
|
1498
|
+
blendMode: (mode) => {
|
|
1499
|
+
const modeMap = {
|
|
1500
|
+
"source-over": "source-over",
|
|
1501
|
+
"NORMAL": "source-over",
|
|
1502
|
+
"lighter": "lighter",
|
|
1503
|
+
"ADD": "lighter",
|
|
1504
|
+
"multiply": "multiply",
|
|
1505
|
+
"MULTIPLY": "multiply",
|
|
1506
|
+
"screen": "screen",
|
|
1507
|
+
"SCREEN": "screen"
|
|
1508
|
+
};
|
|
1509
|
+
const compositeOp = modeMap[mode];
|
|
1510
|
+
if (!compositeOp) {
|
|
1511
|
+
throw new Error(`[Code Mode Protocol Error] Unsupported blend mode: ${mode}. Supported: NORMAL, ADD, MULTIPLY, SCREEN`);
|
|
1512
|
+
}
|
|
1513
|
+
ctx.globalCompositeOperation = compositeOp;
|
|
1514
|
+
},
|
|
1515
|
+
// Color functions
|
|
1516
|
+
fill: (...args) => {
|
|
1517
|
+
fillEnabled = true;
|
|
1518
|
+
currentFill = parseColor(...args);
|
|
1519
|
+
ctx.fillStyle = currentFill;
|
|
1520
|
+
},
|
|
1521
|
+
noFill: () => {
|
|
1522
|
+
fillEnabled = false;
|
|
1523
|
+
},
|
|
1524
|
+
stroke: (...args) => {
|
|
1525
|
+
strokeEnabled = true;
|
|
1526
|
+
currentStroke = parseColor(...args);
|
|
1527
|
+
ctx.strokeStyle = currentStroke;
|
|
1528
|
+
},
|
|
1529
|
+
noStroke: () => {
|
|
1530
|
+
strokeEnabled = false;
|
|
1531
|
+
},
|
|
1532
|
+
strokeWeight: (weight) => {
|
|
1533
|
+
currentStrokeWeight = weight;
|
|
1534
|
+
ctx.lineWidth = weight;
|
|
1535
|
+
},
|
|
1536
|
+
strokeCap: (cap) => {
|
|
1537
|
+
const capMap = {
|
|
1538
|
+
"round": "round",
|
|
1539
|
+
"ROUND": "round",
|
|
1540
|
+
"square": "butt",
|
|
1541
|
+
"SQUARE": "butt",
|
|
1542
|
+
"project": "square",
|
|
1543
|
+
"PROJECT": "square",
|
|
1544
|
+
"butt": "butt"
|
|
1545
|
+
};
|
|
1546
|
+
ctx.lineCap = capMap[cap] || "round";
|
|
1547
|
+
},
|
|
1548
|
+
strokeJoin: (join) => {
|
|
1549
|
+
const joinMap = {
|
|
1550
|
+
"miter": "miter",
|
|
1551
|
+
"MITER": "miter",
|
|
1552
|
+
"bevel": "bevel",
|
|
1553
|
+
"BEVEL": "bevel",
|
|
1554
|
+
"round": "round",
|
|
1555
|
+
"ROUND": "round"
|
|
1556
|
+
};
|
|
1557
|
+
ctx.lineJoin = joinMap[join] || "miter";
|
|
1558
|
+
},
|
|
1559
|
+
colorMode: (mode, max1, max2, max3, maxA) => {
|
|
1560
|
+
colorModeSettings = {
|
|
1561
|
+
mode: mode.toUpperCase(),
|
|
1562
|
+
maxR: max1 ?? 255,
|
|
1563
|
+
maxG: max2 ?? max1 ?? 255,
|
|
1564
|
+
maxB: max3 ?? max1 ?? 255,
|
|
1565
|
+
maxA: maxA ?? 255
|
|
1566
|
+
};
|
|
1567
|
+
},
|
|
1568
|
+
color: (...args) => parseColor(...args),
|
|
1569
|
+
lerpColor: (c1, c2, amt) => {
|
|
1570
|
+
const color1 = parseCssColor(c1) || { r: 0, g: 0, b: 0, a: 1 };
|
|
1571
|
+
const color2 = parseCssColor(c2) || { r: 255, g: 255, b: 255, a: 1 };
|
|
1572
|
+
const r = Math.round(color1.r + (color2.r - color1.r) * amt);
|
|
1573
|
+
const g = Math.round(color1.g + (color2.g - color1.g) * amt);
|
|
1574
|
+
const b = Math.round(color1.b + (color2.b - color1.b) * amt);
|
|
1575
|
+
const a = color1.a + (color2.a - color1.a) * amt;
|
|
1576
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
|
1577
|
+
},
|
|
1578
|
+
red: (color) => {
|
|
1579
|
+
const parsed = parseCssColor(color);
|
|
1580
|
+
return parsed ? parsed.r : 0;
|
|
1581
|
+
},
|
|
1582
|
+
green: (color) => {
|
|
1583
|
+
const parsed = parseCssColor(color);
|
|
1584
|
+
return parsed ? parsed.g : 0;
|
|
1585
|
+
},
|
|
1586
|
+
blue: (color) => {
|
|
1587
|
+
const parsed = parseCssColor(color);
|
|
1588
|
+
return parsed ? parsed.b : 0;
|
|
1589
|
+
},
|
|
1590
|
+
alpha: (color) => {
|
|
1591
|
+
const parsed = parseCssColor(color);
|
|
1592
|
+
return parsed ? parsed.a * 255 : 255;
|
|
1593
|
+
},
|
|
1594
|
+
brightness: (color) => {
|
|
1595
|
+
const parsed = parseCssColor(color);
|
|
1596
|
+
if (!parsed) return 0;
|
|
1597
|
+
return Math.max(parsed.r, parsed.g, parsed.b) / 255 * 100;
|
|
1598
|
+
},
|
|
1599
|
+
saturation: (color) => {
|
|
1600
|
+
const parsed = parseCssColor(color);
|
|
1601
|
+
if (!parsed) return 0;
|
|
1602
|
+
const max = Math.max(parsed.r, parsed.g, parsed.b);
|
|
1603
|
+
const min = Math.min(parsed.r, parsed.g, parsed.b);
|
|
1604
|
+
if (max === 0) return 0;
|
|
1605
|
+
return (max - min) / max * 100;
|
|
1606
|
+
},
|
|
1607
|
+
hue: (color) => {
|
|
1608
|
+
const parsed = parseCssColor(color);
|
|
1609
|
+
if (!parsed) return 0;
|
|
1610
|
+
const { r, g, b } = parsed;
|
|
1611
|
+
const max = Math.max(r, g, b);
|
|
1612
|
+
const min = Math.min(r, g, b);
|
|
1613
|
+
if (max === min) return 0;
|
|
1614
|
+
let h = 0;
|
|
1615
|
+
const d = max - min;
|
|
1616
|
+
switch (max) {
|
|
1617
|
+
case r:
|
|
1618
|
+
h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
|
|
1619
|
+
break;
|
|
1620
|
+
case g:
|
|
1621
|
+
h = ((b - r) / d + 2) / 6;
|
|
1622
|
+
break;
|
|
1623
|
+
case b:
|
|
1624
|
+
h = ((r - g) / d + 4) / 6;
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
return h * 360;
|
|
1628
|
+
},
|
|
1629
|
+
// Shape functions
|
|
1630
|
+
ellipse: (x, y, w, h) => {
|
|
1631
|
+
const rw = w / 2;
|
|
1632
|
+
const rh = (h ?? w) / 2;
|
|
1633
|
+
ctx.beginPath();
|
|
1634
|
+
ctx.ellipse(x, y, rw, rh, 0, 0, Math.PI * 2);
|
|
1635
|
+
if (fillEnabled) ctx.fill();
|
|
1636
|
+
if (strokeEnabled) ctx.stroke();
|
|
1637
|
+
},
|
|
1638
|
+
circle: (x, y, d) => {
|
|
1639
|
+
p.ellipse(x, y, d, d);
|
|
1640
|
+
},
|
|
1641
|
+
rect: (x, y, w, h, r) => {
|
|
1642
|
+
const height2 = h ?? w;
|
|
1643
|
+
ctx.beginPath();
|
|
1644
|
+
if (r && r > 0) {
|
|
1645
|
+
ctx.roundRect(x, y, w, height2, r);
|
|
1646
|
+
} else {
|
|
1647
|
+
ctx.rect(x, y, w, height2);
|
|
1648
|
+
}
|
|
1649
|
+
if (fillEnabled) ctx.fill();
|
|
1650
|
+
if (strokeEnabled) ctx.stroke();
|
|
1651
|
+
},
|
|
1652
|
+
square: (x, y, s, r) => {
|
|
1653
|
+
p.rect(x, y, s, s, r);
|
|
1654
|
+
},
|
|
1655
|
+
line: (x1, y1, x2, y2) => {
|
|
1656
|
+
ctx.beginPath();
|
|
1657
|
+
ctx.moveTo(x1, y1);
|
|
1658
|
+
ctx.lineTo(x2, y2);
|
|
1659
|
+
if (strokeEnabled) ctx.stroke();
|
|
1660
|
+
},
|
|
1661
|
+
point: (x, y) => {
|
|
1662
|
+
ctx.beginPath();
|
|
1663
|
+
ctx.arc(x, y, currentStrokeWeight / 2, 0, Math.PI * 2);
|
|
1664
|
+
ctx.fillStyle = currentStroke;
|
|
1665
|
+
ctx.fill();
|
|
1666
|
+
},
|
|
1667
|
+
triangle: (x1, y1, x2, y2, x3, y3) => {
|
|
1668
|
+
ctx.beginPath();
|
|
1669
|
+
ctx.moveTo(x1, y1);
|
|
1670
|
+
ctx.lineTo(x2, y2);
|
|
1671
|
+
ctx.lineTo(x3, y3);
|
|
1672
|
+
ctx.closePath();
|
|
1673
|
+
if (fillEnabled) ctx.fill();
|
|
1674
|
+
if (strokeEnabled) ctx.stroke();
|
|
1675
|
+
},
|
|
1676
|
+
quad: (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
1677
|
+
ctx.beginPath();
|
|
1678
|
+
ctx.moveTo(x1, y1);
|
|
1679
|
+
ctx.lineTo(x2, y2);
|
|
1680
|
+
ctx.lineTo(x3, y3);
|
|
1681
|
+
ctx.lineTo(x4, y4);
|
|
1682
|
+
ctx.closePath();
|
|
1683
|
+
if (fillEnabled) ctx.fill();
|
|
1684
|
+
if (strokeEnabled) ctx.stroke();
|
|
1685
|
+
},
|
|
1686
|
+
arc: (x, y, w, h, start, stop, mode) => {
|
|
1687
|
+
ctx.beginPath();
|
|
1688
|
+
ctx.ellipse(x, y, w / 2, h / 2, 0, start, stop);
|
|
1689
|
+
if (mode === "pie" || mode === "PIE") {
|
|
1690
|
+
ctx.lineTo(x, y);
|
|
1691
|
+
ctx.closePath();
|
|
1692
|
+
} else if (mode === "chord" || mode === "CHORD") {
|
|
1693
|
+
ctx.closePath();
|
|
1694
|
+
}
|
|
1695
|
+
if (fillEnabled) ctx.fill();
|
|
1696
|
+
if (strokeEnabled) ctx.stroke();
|
|
1697
|
+
},
|
|
1698
|
+
// Bezier and curve functions
|
|
1699
|
+
bezier: (x1, y1, cx1, cy1, cx2, cy2, x2, y2) => {
|
|
1700
|
+
ctx.beginPath();
|
|
1701
|
+
ctx.moveTo(x1, y1);
|
|
1702
|
+
ctx.bezierCurveTo(cx1, cy1, cx2, cy2, x2, y2);
|
|
1703
|
+
if (strokeEnabled) ctx.stroke();
|
|
1704
|
+
},
|
|
1705
|
+
curve: (x1, y1, x2, y2, x3, y3, x4, y4) => {
|
|
1706
|
+
const tension = 1 / 6;
|
|
1707
|
+
const cp1x = x2 + (x3 - x1) * tension;
|
|
1708
|
+
const cp1y = y2 + (y3 - y1) * tension;
|
|
1709
|
+
const cp2x = x3 - (x4 - x2) * tension;
|
|
1710
|
+
const cp2y = y3 - (y4 - y2) * tension;
|
|
1711
|
+
ctx.beginPath();
|
|
1712
|
+
ctx.moveTo(x2, y2);
|
|
1713
|
+
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x3, y3);
|
|
1714
|
+
if (strokeEnabled) ctx.stroke();
|
|
1715
|
+
},
|
|
1716
|
+
// Shape helpers (v1.1)
|
|
1717
|
+
polygon: (cx, cy, radius, sides, rotation = 0) => {
|
|
1718
|
+
ctx.beginPath();
|
|
1719
|
+
for (let i = 0; i < sides; i++) {
|
|
1720
|
+
const angle = rotation + i / sides * Math.PI * 2 - Math.PI / 2;
|
|
1721
|
+
const x = cx + Math.cos(angle) * radius;
|
|
1722
|
+
const y = cy + Math.sin(angle) * radius;
|
|
1723
|
+
if (i === 0) {
|
|
1724
|
+
ctx.moveTo(x, y);
|
|
1725
|
+
} else {
|
|
1726
|
+
ctx.lineTo(x, y);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
ctx.closePath();
|
|
1730
|
+
if (fillEnabled) ctx.fill();
|
|
1731
|
+
if (strokeEnabled) ctx.stroke();
|
|
1732
|
+
},
|
|
1733
|
+
star: (cx, cy, innerRadius, outerRadius, points, rotation = 0) => {
|
|
1734
|
+
ctx.beginPath();
|
|
1735
|
+
const totalPoints = points * 2;
|
|
1736
|
+
for (let i = 0; i < totalPoints; i++) {
|
|
1737
|
+
const angle = rotation + i / totalPoints * Math.PI * 2 - Math.PI / 2;
|
|
1738
|
+
const radius = i % 2 === 0 ? outerRadius : innerRadius;
|
|
1739
|
+
const x = cx + Math.cos(angle) * radius;
|
|
1740
|
+
const y = cy + Math.sin(angle) * radius;
|
|
1741
|
+
if (i === 0) {
|
|
1742
|
+
ctx.moveTo(x, y);
|
|
1743
|
+
} else {
|
|
1744
|
+
ctx.lineTo(x, y);
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
ctx.closePath();
|
|
1748
|
+
if (fillEnabled) ctx.fill();
|
|
1749
|
+
if (strokeEnabled) ctx.stroke();
|
|
1750
|
+
},
|
|
1751
|
+
// Vertex-based shapes
|
|
1752
|
+
beginShape: () => {
|
|
1753
|
+
shapeVertices = [];
|
|
1754
|
+
},
|
|
1755
|
+
vertex: (x, y) => {
|
|
1756
|
+
shapeVertices.push({ x, y, type: "vertex" });
|
|
1757
|
+
},
|
|
1758
|
+
curveVertex: (x, y) => {
|
|
1759
|
+
shapeVertices.push({ x, y, type: "curve" });
|
|
1760
|
+
},
|
|
1761
|
+
bezierVertex: (cx1, cy1, cx2, cy2, x, y) => {
|
|
1762
|
+
shapeVertices.push({ x, y, type: "bezier", cx1, cy1, cx2, cy2 });
|
|
1763
|
+
},
|
|
1764
|
+
endShape: (mode) => {
|
|
1765
|
+
if (shapeVertices.length === 0) return;
|
|
1766
|
+
ctx.beginPath();
|
|
1767
|
+
let started = false;
|
|
1768
|
+
let curveBuffer = [];
|
|
1769
|
+
const tension = 1 / 6;
|
|
1770
|
+
const flushCurveBuffer = () => {
|
|
1771
|
+
if (curveBuffer.length >= 4) {
|
|
1772
|
+
if (!started) {
|
|
1773
|
+
ctx.moveTo(curveBuffer[1].x, curveBuffer[1].y);
|
|
1774
|
+
started = true;
|
|
1775
|
+
} else {
|
|
1776
|
+
ctx.lineTo(curveBuffer[1].x, curveBuffer[1].y);
|
|
1777
|
+
}
|
|
1778
|
+
for (let i = 1; i < curveBuffer.length - 2; i++) {
|
|
1779
|
+
const p0 = curveBuffer[i - 1];
|
|
1780
|
+
const p1 = curveBuffer[i];
|
|
1781
|
+
const p2 = curveBuffer[i + 1];
|
|
1782
|
+
const p3 = curveBuffer[i + 2];
|
|
1783
|
+
const cp1x = p1.x + (p2.x - p0.x) * tension;
|
|
1784
|
+
const cp1y = p1.y + (p2.y - p0.y) * tension;
|
|
1785
|
+
const cp2x = p2.x - (p3.x - p1.x) * tension;
|
|
1786
|
+
const cp2y = p2.y - (p3.y - p1.y) * tension;
|
|
1787
|
+
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, p2.x, p2.y);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
curveBuffer = [];
|
|
1791
|
+
};
|
|
1792
|
+
for (let i = 0; i < shapeVertices.length; i++) {
|
|
1793
|
+
const v = shapeVertices[i];
|
|
1794
|
+
if (v.type === "curve") {
|
|
1795
|
+
curveBuffer.push({ x: v.x, y: v.y });
|
|
1796
|
+
} else {
|
|
1797
|
+
if (curveBuffer.length > 0) {
|
|
1798
|
+
flushCurveBuffer();
|
|
1799
|
+
}
|
|
1800
|
+
if (v.type === "vertex") {
|
|
1801
|
+
if (!started) {
|
|
1802
|
+
ctx.moveTo(v.x, v.y);
|
|
1803
|
+
started = true;
|
|
1804
|
+
} else {
|
|
1805
|
+
ctx.lineTo(v.x, v.y);
|
|
1806
|
+
}
|
|
1807
|
+
} else if (v.type === "bezier" && started) {
|
|
1808
|
+
ctx.bezierCurveTo(v.cx1, v.cy1, v.cx2, v.cy2, v.x, v.y);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
if (curveBuffer.length > 0) {
|
|
1813
|
+
flushCurveBuffer();
|
|
1814
|
+
}
|
|
1815
|
+
if (mode === "close" || mode === "CLOSE") {
|
|
1816
|
+
ctx.closePath();
|
|
1817
|
+
}
|
|
1818
|
+
if (fillEnabled) ctx.fill();
|
|
1819
|
+
if (strokeEnabled) ctx.stroke();
|
|
1820
|
+
shapeVertices = [];
|
|
1821
|
+
},
|
|
1822
|
+
// Transform functions
|
|
1823
|
+
push: () => {
|
|
1824
|
+
ctx.save();
|
|
1825
|
+
},
|
|
1826
|
+
pop: () => {
|
|
1827
|
+
ctx.restore();
|
|
1828
|
+
ctx.fillStyle = currentFill;
|
|
1829
|
+
ctx.strokeStyle = currentStroke;
|
|
1830
|
+
ctx.lineWidth = currentStrokeWeight;
|
|
1831
|
+
},
|
|
1832
|
+
translate: (x, y) => {
|
|
1833
|
+
ctx.translate(x, y);
|
|
1834
|
+
},
|
|
1835
|
+
rotate: (angle) => {
|
|
1836
|
+
ctx.rotate(angle);
|
|
1837
|
+
},
|
|
1838
|
+
scale: (sx, sy) => {
|
|
1839
|
+
ctx.scale(sx, sy ?? sx);
|
|
1840
|
+
},
|
|
1841
|
+
resetMatrix: () => {
|
|
1842
|
+
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
1843
|
+
},
|
|
1844
|
+
shearX: (angle) => {
|
|
1845
|
+
ctx.transform(1, 0, Math.tan(angle), 1, 0, 0);
|
|
1846
|
+
},
|
|
1847
|
+
shearY: (angle) => {
|
|
1848
|
+
ctx.transform(1, Math.tan(angle), 0, 1, 0, 0);
|
|
1849
|
+
},
|
|
1850
|
+
// Math functions - SEEDED for determinism
|
|
1851
|
+
random: (min, max) => {
|
|
1852
|
+
if (Array.isArray(min)) {
|
|
1853
|
+
return min[Math.floor(rng() * min.length)];
|
|
1854
|
+
}
|
|
1855
|
+
if (min === void 0) return rng();
|
|
1856
|
+
if (max === void 0) return rng() * min;
|
|
1857
|
+
return min + rng() * (max - min);
|
|
1858
|
+
},
|
|
1859
|
+
randomSeed: (seed) => {
|
|
1860
|
+
randomSeedValue = seed;
|
|
1861
|
+
rng = createSeededRNG(seed);
|
|
1862
|
+
},
|
|
1863
|
+
randomGaussian: (mean = 0, sd = 1) => {
|
|
1864
|
+
const u1 = rng();
|
|
1865
|
+
const u2 = rng();
|
|
1866
|
+
const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
1867
|
+
return z0 * sd + mean;
|
|
1868
|
+
},
|
|
1869
|
+
noise: (x, y, z) => {
|
|
1870
|
+
let total = 0;
|
|
1871
|
+
let frequency = 1;
|
|
1872
|
+
let amplitude = 1;
|
|
1873
|
+
let maxValue = 0;
|
|
1874
|
+
for (let i = 0; i < noiseOctaves; i++) {
|
|
1875
|
+
total += noiseFunc(x * frequency, (y ?? 0) * frequency, (z ?? 0) * frequency) * amplitude;
|
|
1876
|
+
maxValue += amplitude;
|
|
1877
|
+
amplitude *= noiseFalloff;
|
|
1878
|
+
frequency *= 2;
|
|
1879
|
+
}
|
|
1880
|
+
return total / maxValue;
|
|
1881
|
+
},
|
|
1882
|
+
noiseSeed: (seed) => {
|
|
1883
|
+
noiseSeedValue = seed;
|
|
1884
|
+
noiseFunc = createSeededNoise(seed);
|
|
1885
|
+
},
|
|
1886
|
+
noiseDetail: (lod, falloff) => {
|
|
1887
|
+
noiseOctaves = Math.max(1, Math.min(8, lod));
|
|
1888
|
+
if (falloff !== void 0) {
|
|
1889
|
+
noiseFalloff = Math.max(0, Math.min(1, falloff));
|
|
1890
|
+
}
|
|
1891
|
+
},
|
|
1892
|
+
// Noise extensions (v1.1) - use seeded noise internally
|
|
1893
|
+
fbm: (x, y, octaves = 4, falloff = 0.5) => {
|
|
1894
|
+
let total = 0;
|
|
1895
|
+
let frequency = 1;
|
|
1896
|
+
let amplitude = 1;
|
|
1897
|
+
let maxValue = 0;
|
|
1898
|
+
for (let i = 0; i < octaves; i++) {
|
|
1899
|
+
total += noiseFunc(x * frequency, y * frequency) * amplitude;
|
|
1900
|
+
maxValue += amplitude;
|
|
1901
|
+
amplitude *= falloff;
|
|
1902
|
+
frequency *= 2;
|
|
1903
|
+
}
|
|
1904
|
+
return total / maxValue;
|
|
1905
|
+
},
|
|
1906
|
+
ridgedNoise: (x, y) => {
|
|
1907
|
+
let total = 0;
|
|
1908
|
+
let frequency = 1;
|
|
1909
|
+
let amplitude = 1;
|
|
1910
|
+
let maxValue = 0;
|
|
1911
|
+
for (let i = 0; i < 4; i++) {
|
|
1912
|
+
const n = noiseFunc(x * frequency, y * frequency);
|
|
1913
|
+
total += (1 - Math.abs(n * 2 - 1)) * amplitude;
|
|
1914
|
+
maxValue += amplitude;
|
|
1915
|
+
amplitude *= 0.5;
|
|
1916
|
+
frequency *= 2;
|
|
1917
|
+
}
|
|
1918
|
+
return total / maxValue;
|
|
1919
|
+
},
|
|
1920
|
+
curlNoise: (x, y) => {
|
|
1921
|
+
const eps = 1e-4;
|
|
1922
|
+
const n1 = noiseFunc(x + eps, y);
|
|
1923
|
+
const n2 = noiseFunc(x - eps, y);
|
|
1924
|
+
const n3 = noiseFunc(x, y + eps);
|
|
1925
|
+
const n4 = noiseFunc(x, y - eps);
|
|
1926
|
+
const dx = (n1 - n2) / (2 * eps);
|
|
1927
|
+
const dy = (n3 - n4) / (2 * eps);
|
|
1928
|
+
return { x: -dy, y: dx };
|
|
1929
|
+
},
|
|
1930
|
+
map: (value, start1, stop1, start2, stop2) => {
|
|
1931
|
+
return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
|
|
1932
|
+
},
|
|
1933
|
+
constrain: (n, low, high) => {
|
|
1934
|
+
return Math.max(low, Math.min(high, n));
|
|
1935
|
+
},
|
|
1936
|
+
lerp: (start, stop, amt) => {
|
|
1937
|
+
return start + (stop - start) * amt;
|
|
1938
|
+
},
|
|
1939
|
+
dist: (x1, y1, x2, y2) => {
|
|
1940
|
+
return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
|
|
1941
|
+
},
|
|
1942
|
+
mag: (x, y) => {
|
|
1943
|
+
return Math.sqrt(x * x + y * y);
|
|
1944
|
+
},
|
|
1945
|
+
norm: (value, start, stop) => {
|
|
1946
|
+
return (value - start) / (stop - start);
|
|
1947
|
+
},
|
|
1948
|
+
// Trig functions
|
|
1949
|
+
sin: Math.sin,
|
|
1950
|
+
cos: Math.cos,
|
|
1951
|
+
tan: Math.tan,
|
|
1952
|
+
asin: Math.asin,
|
|
1953
|
+
acos: Math.acos,
|
|
1954
|
+
atan: Math.atan,
|
|
1955
|
+
atan2: Math.atan2,
|
|
1956
|
+
radians: (degrees) => degrees * (Math.PI / 180),
|
|
1957
|
+
degrees: (radians) => radians * (180 / Math.PI),
|
|
1958
|
+
// Utility functions
|
|
1959
|
+
abs: Math.abs,
|
|
1960
|
+
ceil: Math.ceil,
|
|
1961
|
+
floor: Math.floor,
|
|
1962
|
+
round: Math.round,
|
|
1963
|
+
sqrt: Math.sqrt,
|
|
1964
|
+
pow: Math.pow,
|
|
1965
|
+
exp: Math.exp,
|
|
1966
|
+
log: Math.log,
|
|
1967
|
+
min: Math.min,
|
|
1968
|
+
max: Math.max,
|
|
1969
|
+
int: (n) => Math.floor(n),
|
|
1970
|
+
sq: (n) => n * n,
|
|
1971
|
+
fract: (n) => n - Math.floor(n),
|
|
1972
|
+
sign: (n) => n > 0 ? 1 : n < 0 ? -1 : 0,
|
|
1973
|
+
// Vector helpers (v1.1) - plain objects, no mutation
|
|
1974
|
+
vec: (x, y) => ({ x, y }),
|
|
1975
|
+
vecAdd: (a, b) => ({
|
|
1976
|
+
x: a.x + b.x,
|
|
1977
|
+
y: a.y + b.y
|
|
1978
|
+
}),
|
|
1979
|
+
vecSub: (a, b) => ({
|
|
1980
|
+
x: a.x - b.x,
|
|
1981
|
+
y: a.y - b.y
|
|
1982
|
+
}),
|
|
1983
|
+
vecMult: (v, s) => ({
|
|
1984
|
+
x: v.x * s,
|
|
1985
|
+
y: v.y * s
|
|
1986
|
+
}),
|
|
1987
|
+
vecMag: (v) => Math.sqrt(v.x * v.x + v.y * v.y),
|
|
1988
|
+
vecNorm: (v) => {
|
|
1989
|
+
const m = Math.sqrt(v.x * v.x + v.y * v.y);
|
|
1990
|
+
return m === 0 ? { x: 0, y: 0 } : { x: v.x / m, y: v.y / m };
|
|
1991
|
+
},
|
|
1992
|
+
vecDist: (a, b) => {
|
|
1993
|
+
const dx = b.x - a.x;
|
|
1994
|
+
const dy = b.y - a.y;
|
|
1995
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
1996
|
+
},
|
|
1997
|
+
// Easing functions (v1.1) - pure functions, t ∈ [0,1] → [0,1]
|
|
1998
|
+
easeIn: (t) => t * t,
|
|
1999
|
+
easeOut: (t) => t * (2 - t),
|
|
2000
|
+
easeInOut: (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
|
|
2001
|
+
easeCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2,
|
|
2002
|
+
easeExpo: (t) => {
|
|
2003
|
+
if (t === 0) return 0;
|
|
2004
|
+
if (t === 1) return 1;
|
|
2005
|
+
if (t < 0.5) {
|
|
2006
|
+
return Math.pow(2, 20 * t - 10) / 2;
|
|
2007
|
+
}
|
|
2008
|
+
return (2 - Math.pow(2, -20 * t + 10)) / 2;
|
|
2009
|
+
},
|
|
2010
|
+
// Text functions
|
|
2011
|
+
text: (str, x, y) => {
|
|
2012
|
+
ctx.font = `${currentTextSize}px ${currentTextFont}`;
|
|
2013
|
+
ctx.textAlign = currentTextAlignH;
|
|
2014
|
+
ctx.textBaseline = currentTextAlignV;
|
|
2015
|
+
if (fillEnabled) {
|
|
2016
|
+
ctx.fillStyle = currentFill;
|
|
2017
|
+
ctx.fillText(String(str), x, y);
|
|
2018
|
+
}
|
|
2019
|
+
if (strokeEnabled) {
|
|
2020
|
+
ctx.strokeStyle = currentStroke;
|
|
2021
|
+
ctx.strokeText(String(str), x, y);
|
|
2022
|
+
}
|
|
2023
|
+
},
|
|
2024
|
+
textSize: (size) => {
|
|
2025
|
+
currentTextSize = size;
|
|
2026
|
+
ctx.font = `${currentTextSize}px ${currentTextFont}`;
|
|
2027
|
+
},
|
|
2028
|
+
textFont: (font) => {
|
|
2029
|
+
currentTextFont = font;
|
|
2030
|
+
ctx.font = `${currentTextSize}px ${currentTextFont}`;
|
|
2031
|
+
},
|
|
2032
|
+
textAlign: (horizAlign, vertAlign) => {
|
|
2033
|
+
const hMap = {
|
|
2034
|
+
"left": "left",
|
|
2035
|
+
"LEFT": "left",
|
|
2036
|
+
"center": "center",
|
|
2037
|
+
"CENTER": "center",
|
|
2038
|
+
"right": "right",
|
|
2039
|
+
"RIGHT": "right"
|
|
2040
|
+
};
|
|
2041
|
+
const vMap = {
|
|
2042
|
+
"top": "top",
|
|
2043
|
+
"TOP": "top",
|
|
2044
|
+
"bottom": "bottom",
|
|
2045
|
+
"BOTTOM": "bottom",
|
|
2046
|
+
"center": "middle",
|
|
2047
|
+
"CENTER": "middle",
|
|
2048
|
+
"baseline": "alphabetic",
|
|
2049
|
+
"BASELINE": "alphabetic"
|
|
2050
|
+
};
|
|
2051
|
+
currentTextAlignH = hMap[horizAlign] || "left";
|
|
2052
|
+
if (vertAlign) {
|
|
2053
|
+
currentTextAlignV = vMap[vertAlign] || "alphabetic";
|
|
2054
|
+
}
|
|
2055
|
+
},
|
|
2056
|
+
textWidth: (str) => {
|
|
2057
|
+
ctx.font = `${currentTextSize}px ${currentTextFont}`;
|
|
2058
|
+
return ctx.measureText(String(str)).width;
|
|
2059
|
+
},
|
|
2060
|
+
// Pixel manipulation (v1.2)
|
|
2061
|
+
loadPixels: () => {
|
|
2062
|
+
imageDataObj = ctx.getImageData(0, 0, width, height);
|
|
2063
|
+
pixelData = imageDataObj.data;
|
|
2064
|
+
p.pixels = pixelData;
|
|
2065
|
+
},
|
|
2066
|
+
updatePixels: () => {
|
|
2067
|
+
if (imageDataObj && pixelData) {
|
|
2068
|
+
ctx.putImageData(imageDataObj, 0, 0);
|
|
2069
|
+
}
|
|
2070
|
+
},
|
|
2071
|
+
pixels: null,
|
|
2072
|
+
get: (x, y) => {
|
|
2073
|
+
const imgData = ctx.getImageData(Math.floor(x), Math.floor(y), 1, 1);
|
|
2074
|
+
return [imgData.data[0], imgData.data[1], imgData.data[2], imgData.data[3]];
|
|
2075
|
+
},
|
|
2076
|
+
set: (x, y, c) => {
|
|
2077
|
+
const fx = Math.floor(x);
|
|
2078
|
+
const fy = Math.floor(y);
|
|
2079
|
+
if (Array.isArray(c)) {
|
|
2080
|
+
const imgData = ctx.createImageData(1, 1);
|
|
2081
|
+
imgData.data[0] = c[0];
|
|
2082
|
+
imgData.data[1] = c[1];
|
|
2083
|
+
imgData.data[2] = c[2];
|
|
2084
|
+
imgData.data[3] = c[3] ?? 255;
|
|
2085
|
+
ctx.putImageData(imgData, fx, fy);
|
|
2086
|
+
} else {
|
|
2087
|
+
const parsed = parseCssColor(c);
|
|
2088
|
+
if (parsed) {
|
|
2089
|
+
const imgData = ctx.createImageData(1, 1);
|
|
2090
|
+
imgData.data[0] = parsed.r;
|
|
2091
|
+
imgData.data[1] = parsed.g;
|
|
2092
|
+
imgData.data[2] = parsed.b;
|
|
2093
|
+
imgData.data[3] = Math.round(parsed.a * 255);
|
|
2094
|
+
ctx.putImageData(imgData, fx, fy);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
// Offscreen graphics (v1.2)
|
|
2099
|
+
createGraphics: (w, h) => {
|
|
2100
|
+
const offscreenCanvas = document.createElement("canvas");
|
|
2101
|
+
offscreenCanvas.width = w;
|
|
2102
|
+
offscreenCanvas.height = h;
|
|
2103
|
+
const pg = createP5Runtime(offscreenCanvas, w, h, config);
|
|
2104
|
+
pg._canvas = offscreenCanvas;
|
|
2105
|
+
return pg;
|
|
2106
|
+
},
|
|
2107
|
+
// Draw image or graphics object to canvas
|
|
2108
|
+
image: (src, x, y, w, h) => {
|
|
2109
|
+
const srcCanvas = src._canvas || src;
|
|
2110
|
+
if (srcCanvas instanceof HTMLCanvasElement) {
|
|
2111
|
+
const dw = w ?? srcCanvas.width;
|
|
2112
|
+
const dh = h ?? srcCanvas.height;
|
|
2113
|
+
ctx.drawImage(srcCanvas, x, y, dw, dh);
|
|
2114
|
+
}
|
|
2115
|
+
},
|
|
2116
|
+
// Loop control (no-ops for SDK)
|
|
2117
|
+
noLoop: () => {
|
|
2118
|
+
},
|
|
2119
|
+
loop: () => {
|
|
2120
|
+
},
|
|
2121
|
+
redraw: () => {
|
|
2122
|
+
},
|
|
2123
|
+
frameRate: (fps) => {
|
|
2124
|
+
},
|
|
2125
|
+
// totalFrames placeholder (injected by engine)
|
|
2126
|
+
totalFrames: 0
|
|
2127
|
+
};
|
|
2128
|
+
return p;
|
|
2129
|
+
}
|
|
2130
|
+
function injectTimeVariables(p, time) {
|
|
2131
|
+
p.frameCount = time.frameCount;
|
|
2132
|
+
p.t = time.t;
|
|
2133
|
+
p.time = time.time;
|
|
2134
|
+
p.tGlobal = time.tGlobal;
|
|
2135
|
+
p.totalFrames = time.totalFrames;
|
|
2136
|
+
}
|
|
2137
|
+
var VAR_COUNT = 10;
|
|
2138
|
+
var VAR_MIN = 0;
|
|
2139
|
+
var VAR_MAX = 100;
|
|
2140
|
+
function createProtocolVAR(vars) {
|
|
2141
|
+
const normalizedVars = [];
|
|
2142
|
+
for (let i = 0; i < VAR_COUNT; i++) {
|
|
2143
|
+
normalizedVars[i] = vars?.[i] ?? 0;
|
|
2144
|
+
}
|
|
2145
|
+
const frozenVars = Object.freeze(normalizedVars);
|
|
2146
|
+
return new Proxy(frozenVars, {
|
|
2147
|
+
set(_target, prop, _value) {
|
|
2148
|
+
const propName = typeof prop === "symbol" ? prop.toString() : prop;
|
|
2149
|
+
throw new Error(
|
|
2150
|
+
`[Code Mode Protocol Error] VAR is read-only. Cannot write to VAR[${propName}]. VAR[0..9] are protocol inputs, not sketch state.`
|
|
2151
|
+
);
|
|
2152
|
+
},
|
|
2153
|
+
deleteProperty(_target, prop) {
|
|
2154
|
+
const propName = typeof prop === "symbol" ? prop.toString() : prop;
|
|
2155
|
+
throw new Error(
|
|
2156
|
+
`[Code Mode Protocol Error] VAR is read-only. Cannot delete VAR[${propName}].`
|
|
2157
|
+
);
|
|
2158
|
+
},
|
|
2159
|
+
defineProperty(_target, prop) {
|
|
2160
|
+
const propName = typeof prop === "symbol" ? prop.toString() : prop;
|
|
2161
|
+
throw new Error(
|
|
2162
|
+
`[Code Mode Protocol Error] Cannot define new VAR properties. VAR is fixed at 10 elements (VAR[0..9]). Attempted: ${propName}`
|
|
2163
|
+
);
|
|
2164
|
+
}
|
|
2165
|
+
});
|
|
2166
|
+
}
|
|
2167
|
+
function injectProtocolVariables(p, vars) {
|
|
2168
|
+
p.VAR = createProtocolVAR(vars);
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
// loop-engine.ts
|
|
2172
|
+
var isCancelled = false;
|
|
2173
|
+
function cancelLoopMode() {
|
|
2174
|
+
isCancelled = true;
|
|
2175
|
+
}
|
|
2176
|
+
async function runLoopMode(config, options) {
|
|
2177
|
+
const { code, seed, vars, onPreview, onProgress, onComplete, onError } = options;
|
|
2178
|
+
const width = config.width ?? DEFAULT_CONFIG.width;
|
|
2179
|
+
const height = config.height ?? DEFAULT_CONFIG.height;
|
|
2180
|
+
const duration = Math.max(
|
|
2181
|
+
DEFAULT_CONFIG.minDuration,
|
|
2182
|
+
Math.min(DEFAULT_CONFIG.maxDuration, config.duration ?? DEFAULT_CONFIG.duration)
|
|
2183
|
+
);
|
|
2184
|
+
const fps = config.fps ?? DEFAULT_CONFIG.fps;
|
|
2185
|
+
const totalFrames = Math.floor(duration * fps);
|
|
2186
|
+
isCancelled = false;
|
|
2187
|
+
try {
|
|
2188
|
+
onProgress?.({
|
|
2189
|
+
phase: "setup",
|
|
2190
|
+
percent: 0,
|
|
2191
|
+
message: "Initializing canvas..."
|
|
2192
|
+
});
|
|
2193
|
+
const canvas = document.createElement("canvas");
|
|
2194
|
+
canvas.width = width;
|
|
2195
|
+
canvas.height = height;
|
|
2196
|
+
const p = createP5Runtime(canvas, width, height, { seed });
|
|
2197
|
+
injectProtocolVariables(p, vars);
|
|
2198
|
+
const hasDrawFunction = /function\s+draw\s*\(\s*\)/.test(code);
|
|
2199
|
+
if (!hasDrawFunction) {
|
|
2200
|
+
throw new Error("Loop Mode requires a draw() function.");
|
|
2201
|
+
}
|
|
2202
|
+
const forbiddenPatterns = [
|
|
2203
|
+
{ pattern: /noLoop\s*\(\s*\)/, name: "noLoop()" },
|
|
2204
|
+
{ pattern: /setTimeout\s*\(/, name: "setTimeout" },
|
|
2205
|
+
{ pattern: /setInterval\s*\(/, name: "setInterval" },
|
|
2206
|
+
{ pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" }
|
|
2207
|
+
];
|
|
2208
|
+
for (const { pattern, name } of forbiddenPatterns) {
|
|
2209
|
+
if (pattern.test(code)) {
|
|
2210
|
+
throw new Error(`Forbidden function in Loop Mode: ${name}`);
|
|
2211
|
+
}
|
|
2212
|
+
}
|
|
2213
|
+
onProgress?.({
|
|
2214
|
+
phase: "setup",
|
|
2215
|
+
percent: 5,
|
|
2216
|
+
message: "Parsing code..."
|
|
2217
|
+
});
|
|
2218
|
+
const setupMatch = code.match(/function\s+setup\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
|
|
2219
|
+
const drawMatch = code.match(/function\s+draw\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
|
|
2220
|
+
const setupCode = setupMatch ? setupMatch[1].trim() : "";
|
|
2221
|
+
const drawCode = drawMatch ? drawMatch[1].trim() : "";
|
|
2222
|
+
if (!drawCode) {
|
|
2223
|
+
throw new Error("Loop Mode requires a draw() function with content.");
|
|
2224
|
+
}
|
|
2225
|
+
p.totalFrames = totalFrames;
|
|
2226
|
+
const safeMath = createSafeMath();
|
|
2227
|
+
const forbiddenKeys = Object.keys(FORBIDDEN_APIS);
|
|
2228
|
+
const wrappedSetup = new Function(
|
|
2229
|
+
"p",
|
|
2230
|
+
"frameCount",
|
|
2231
|
+
"t",
|
|
2232
|
+
"time",
|
|
2233
|
+
"tGlobal",
|
|
2234
|
+
"VAR",
|
|
2235
|
+
"totalFrames",
|
|
2236
|
+
"Math",
|
|
2237
|
+
...forbiddenKeys,
|
|
2238
|
+
`with(p) { ${setupCode} }`
|
|
2239
|
+
);
|
|
2240
|
+
const wrappedDraw = new Function(
|
|
2241
|
+
"p",
|
|
2242
|
+
"frameCount",
|
|
2243
|
+
"t",
|
|
2244
|
+
"time",
|
|
2245
|
+
"tGlobal",
|
|
2246
|
+
"VAR",
|
|
2247
|
+
"totalFrames",
|
|
2248
|
+
"Math",
|
|
2249
|
+
...forbiddenKeys,
|
|
2250
|
+
`with(p) { ${drawCode} }`
|
|
2251
|
+
);
|
|
2252
|
+
const forbiddenValues = forbiddenKeys.map((k) => FORBIDDEN_APIS[k]);
|
|
2253
|
+
onProgress?.({
|
|
2254
|
+
phase: "setup",
|
|
2255
|
+
percent: 10,
|
|
2256
|
+
message: "Executing setup()..."
|
|
2257
|
+
});
|
|
2258
|
+
wrappedSetup(p, 0, 0, 0, 0, p.VAR, totalFrames, safeMath, ...forbiddenValues);
|
|
2259
|
+
const frames = [];
|
|
2260
|
+
onProgress?.({
|
|
2261
|
+
phase: "rendering",
|
|
2262
|
+
frame: 0,
|
|
2263
|
+
totalFrames,
|
|
2264
|
+
percent: 10,
|
|
2265
|
+
message: `Rendering frames (0/${totalFrames})...`
|
|
2266
|
+
});
|
|
2267
|
+
for (let frame = 0; frame < totalFrames; frame++) {
|
|
2268
|
+
if (isCancelled) {
|
|
2269
|
+
throw new Error("Rendering cancelled");
|
|
2270
|
+
}
|
|
2271
|
+
const t = frame / totalFrames;
|
|
2272
|
+
const time = t * duration;
|
|
2273
|
+
p.frameCount = frame;
|
|
2274
|
+
p.clear();
|
|
2275
|
+
p.blendMode("NORMAL");
|
|
2276
|
+
wrappedDraw(p, frame, t, time, t, p.VAR, totalFrames, safeMath, ...forbiddenValues);
|
|
2277
|
+
const blob = await new Promise((resolve, reject) => {
|
|
2278
|
+
canvas.toBlob(
|
|
2279
|
+
(b) => b ? resolve(b) : reject(new Error(`Failed to capture frame ${frame}`)),
|
|
2280
|
+
"image/png"
|
|
2281
|
+
);
|
|
2282
|
+
});
|
|
2283
|
+
frames.push(blob);
|
|
2284
|
+
if (frame === 0) {
|
|
2285
|
+
onPreview?.(canvas);
|
|
2286
|
+
}
|
|
2287
|
+
const percent = 10 + Math.floor(frame / totalFrames * 60);
|
|
2288
|
+
onProgress?.({
|
|
2289
|
+
phase: "rendering",
|
|
2290
|
+
frame: frame + 1,
|
|
2291
|
+
totalFrames,
|
|
2292
|
+
percent,
|
|
2293
|
+
message: `Rendering frames (${frame + 1}/${totalFrames})...`
|
|
2294
|
+
});
|
|
2295
|
+
if (frame % 10 === 0) {
|
|
2296
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
onProgress?.({
|
|
2300
|
+
phase: "encoding",
|
|
2301
|
+
frame: totalFrames,
|
|
2302
|
+
totalFrames,
|
|
2303
|
+
percent: 70,
|
|
2304
|
+
message: "Encoding video..."
|
|
2305
|
+
});
|
|
2306
|
+
const videoBlob = await encodeFramesToMP4(frames, fps, width, height, (progress) => {
|
|
2307
|
+
const percent = 70 + Math.floor(progress * 30);
|
|
2308
|
+
onProgress?.({
|
|
2309
|
+
phase: "encoding",
|
|
2310
|
+
frame: totalFrames,
|
|
2311
|
+
totalFrames,
|
|
2312
|
+
percent,
|
|
2313
|
+
message: `Encoding video (${Math.floor(progress * 100)}%)...`
|
|
2314
|
+
});
|
|
2315
|
+
});
|
|
2316
|
+
onProgress?.({
|
|
2317
|
+
phase: "complete",
|
|
2318
|
+
frame: totalFrames,
|
|
2319
|
+
totalFrames,
|
|
2320
|
+
percent: 100,
|
|
2321
|
+
message: "Complete"
|
|
2322
|
+
});
|
|
2323
|
+
const result = {
|
|
2324
|
+
type: "video",
|
|
2325
|
+
blob: videoBlob,
|
|
2326
|
+
frames: totalFrames,
|
|
2327
|
+
duration
|
|
2328
|
+
};
|
|
2329
|
+
onComplete(result);
|
|
2330
|
+
} catch (error) {
|
|
2331
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2332
|
+
onError?.(err);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
async function encodeFramesToMP4(frames, fps, width, height, onProgress) {
|
|
2336
|
+
const frameDataUrls = [];
|
|
2337
|
+
for (let i = 0; i < frames.length; i++) {
|
|
2338
|
+
const reader = new FileReader();
|
|
2339
|
+
const dataUrl = await new Promise((resolve, reject) => {
|
|
2340
|
+
reader.onload = () => resolve(reader.result);
|
|
2341
|
+
reader.onerror = reject;
|
|
2342
|
+
reader.readAsDataURL(frames[i]);
|
|
2343
|
+
});
|
|
2344
|
+
frameDataUrls.push(dataUrl);
|
|
2345
|
+
onProgress?.(i / frames.length * 0.3);
|
|
2346
|
+
}
|
|
2347
|
+
const response = await fetch("/api/encode-loop", {
|
|
2348
|
+
method: "POST",
|
|
2349
|
+
headers: { "Content-Type": "application/json" },
|
|
2350
|
+
body: JSON.stringify({
|
|
2351
|
+
frames: frameDataUrls,
|
|
2352
|
+
fps,
|
|
2353
|
+
width,
|
|
2354
|
+
height
|
|
2355
|
+
})
|
|
2356
|
+
});
|
|
2357
|
+
if (!response.ok) {
|
|
2358
|
+
const errorText = await response.text();
|
|
2359
|
+
throw new Error(`Video encoding failed: ${errorText}`);
|
|
2360
|
+
}
|
|
2361
|
+
onProgress?.(0.8);
|
|
2362
|
+
const data = await response.json();
|
|
2363
|
+
if (!data.video) {
|
|
2364
|
+
throw new Error("No video data returned from encoder");
|
|
2365
|
+
}
|
|
2366
|
+
const binaryString = atob(data.video.split(",")[1] || data.video);
|
|
2367
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
2368
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
2369
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
2370
|
+
}
|
|
2371
|
+
onProgress?.(1);
|
|
2372
|
+
return new Blob([bytes], { type: "video/mp4" });
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
// static-engine.ts
|
|
2376
|
+
var nodeCanvasModule = null;
|
|
2377
|
+
async function getNodeCanvas() {
|
|
2378
|
+
if (nodeCanvasModule) return nodeCanvasModule;
|
|
2379
|
+
if (typeof window === "undefined") {
|
|
2380
|
+
try {
|
|
2381
|
+
const { createRequire } = await import('module');
|
|
2382
|
+
const require2 = createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('node.cjs', document.baseURI).href)));
|
|
2383
|
+
nodeCanvasModule = require2("canvas");
|
|
2384
|
+
return nodeCanvasModule;
|
|
2385
|
+
} catch {
|
|
2386
|
+
return null;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
return null;
|
|
2390
|
+
}
|
|
2391
|
+
async function createRuntimeCanvas(width, height) {
|
|
2392
|
+
if (typeof document !== "undefined" && typeof document.createElement === "function") {
|
|
2393
|
+
const canvas = document.createElement("canvas");
|
|
2394
|
+
canvas.width = width;
|
|
2395
|
+
canvas.height = height;
|
|
2396
|
+
return canvas;
|
|
2397
|
+
}
|
|
2398
|
+
const nodeCanvas = await getNodeCanvas();
|
|
2399
|
+
if (nodeCanvas && nodeCanvas.createCanvas) {
|
|
2400
|
+
return nodeCanvas.createCanvas(width, height);
|
|
2401
|
+
}
|
|
2402
|
+
throw new Error(
|
|
2403
|
+
"[Code Mode Protocol Error] Headless canvas unavailable. Install `canvas` for oracle execution."
|
|
2404
|
+
);
|
|
2405
|
+
}
|
|
2406
|
+
async function runStaticMode(config, options) {
|
|
2407
|
+
const { code, seed, vars, onPreview, onProgress, onComplete, onError, returnImageData } = options;
|
|
2408
|
+
const width = config.width ?? DEFAULT_CONFIG.width;
|
|
2409
|
+
const height = config.height ?? DEFAULT_CONFIG.height;
|
|
2410
|
+
try {
|
|
2411
|
+
onProgress?.({
|
|
2412
|
+
phase: "setup",
|
|
2413
|
+
percent: 0,
|
|
2414
|
+
message: "Initializing canvas..."
|
|
2415
|
+
});
|
|
2416
|
+
const canvas = await createRuntimeCanvas(width, height);
|
|
2417
|
+
const p = createP5Runtime(canvas, width, height, { seed });
|
|
2418
|
+
injectTimeVariables(p, {
|
|
2419
|
+
frameCount: 0,
|
|
2420
|
+
t: 0,
|
|
2421
|
+
time: 0,
|
|
2422
|
+
tGlobal: 0,
|
|
2423
|
+
totalFrames: 1
|
|
2424
|
+
// Static mode has 1 frame
|
|
2425
|
+
});
|
|
2426
|
+
injectProtocolVariables(p, vars);
|
|
2427
|
+
onProgress?.({
|
|
2428
|
+
phase: "setup",
|
|
2429
|
+
percent: 10,
|
|
2430
|
+
message: "Parsing code..."
|
|
2431
|
+
});
|
|
2432
|
+
const setupMatch = code.match(/function\s+setup\s*\(\s*\)\s*\{([\s\S]*?)\}(?=\s*function|\s*$)/);
|
|
2433
|
+
const setupCode = setupMatch ? setupMatch[1].trim() : code;
|
|
2434
|
+
const forbiddenPatterns = ["setTimeout", "setInterval", "requestAnimationFrame"];
|
|
2435
|
+
for (const pattern of forbiddenPatterns) {
|
|
2436
|
+
if (code.includes(pattern)) {
|
|
2437
|
+
throw new Error(`Forbidden async timing function: ${pattern}`);
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
2440
|
+
onProgress?.({
|
|
2441
|
+
phase: "rendering",
|
|
2442
|
+
percent: 30,
|
|
2443
|
+
message: "Executing setup()..."
|
|
2444
|
+
});
|
|
2445
|
+
const safeMath = createSafeMath();
|
|
2446
|
+
const forbiddenKeys = Object.keys(FORBIDDEN_APIS);
|
|
2447
|
+
const wrappedSetup = new Function(
|
|
2448
|
+
"p",
|
|
2449
|
+
"frameCount",
|
|
2450
|
+
"t",
|
|
2451
|
+
"time",
|
|
2452
|
+
"tGlobal",
|
|
2453
|
+
"VAR",
|
|
2454
|
+
"Math",
|
|
2455
|
+
...forbiddenKeys,
|
|
2456
|
+
`with(p) { ${setupCode} }`
|
|
2457
|
+
);
|
|
2458
|
+
const forbiddenValues = forbiddenKeys.map((k) => FORBIDDEN_APIS[k]);
|
|
2459
|
+
wrappedSetup(p, 0, 0, 0, 0, p.VAR, safeMath, ...forbiddenValues);
|
|
2460
|
+
onPreview?.(canvas);
|
|
2461
|
+
onProgress?.({
|
|
2462
|
+
phase: "encoding",
|
|
2463
|
+
percent: 70,
|
|
2464
|
+
message: returnImageData ? "Capturing ImageData..." : "Capturing PNG..."
|
|
2465
|
+
});
|
|
2466
|
+
const ctx = canvas.getContext("2d");
|
|
2467
|
+
if (!ctx) {
|
|
2468
|
+
throw new Error("Failed to acquire 2D context");
|
|
2469
|
+
}
|
|
2470
|
+
const imageData = ctx.getImageData(0, 0, width, height);
|
|
2471
|
+
if (returnImageData) {
|
|
2472
|
+
onProgress?.({
|
|
2473
|
+
phase: "complete",
|
|
2474
|
+
percent: 100,
|
|
2475
|
+
message: "Complete"
|
|
2476
|
+
});
|
|
2477
|
+
onComplete({
|
|
2478
|
+
type: "image",
|
|
2479
|
+
imageData
|
|
2480
|
+
});
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2483
|
+
const blob = await new Promise((resolve, reject) => {
|
|
2484
|
+
canvas.toBlob(
|
|
2485
|
+
(b) => b ? resolve(b) : reject(new Error("Failed to capture PNG")),
|
|
2486
|
+
"image/png"
|
|
2487
|
+
);
|
|
2488
|
+
});
|
|
2489
|
+
onProgress?.({
|
|
2490
|
+
phase: "complete",
|
|
2491
|
+
percent: 100,
|
|
2492
|
+
message: "Complete"
|
|
2493
|
+
});
|
|
2494
|
+
onComplete({
|
|
2495
|
+
type: "image",
|
|
2496
|
+
blob
|
|
2497
|
+
});
|
|
2498
|
+
} catch (error) {
|
|
2499
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2500
|
+
onError?.(err);
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function registerBuilderManifest(manifest) {
|
|
2504
|
+
if (!manifest) {
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
if (manifest.protocol !== "nexart") {
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (typeof manifest.manifestVersion !== "string") {
|
|
2511
|
+
return;
|
|
2512
|
+
}
|
|
2513
|
+
}
|
|
2514
|
+
|
|
2515
|
+
// execute.ts
|
|
2516
|
+
function normalizeVars(vars) {
|
|
2517
|
+
if (!vars || !Array.isArray(vars)) {
|
|
2518
|
+
console.log("[CodeMode] No vars provided, using defaults [0,0,0,0,0,0,0,0,0,0]");
|
|
2519
|
+
return [0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
|
2520
|
+
}
|
|
2521
|
+
if (vars.length > 10) {
|
|
2522
|
+
throw new Error(`[Code Mode Protocol Error] VAR array must have at most 10 elements, got ${vars.length}`);
|
|
2523
|
+
}
|
|
2524
|
+
const result = [];
|
|
2525
|
+
for (let i = 0; i < vars.length; i++) {
|
|
2526
|
+
const v = vars[i];
|
|
2527
|
+
if (typeof v !== "number" || !Number.isFinite(v)) {
|
|
2528
|
+
throw new Error(`[Code Mode Protocol Error] VAR[${i}] must be a finite number, got ${typeof v === "number" ? v : typeof v}`);
|
|
2529
|
+
}
|
|
2530
|
+
if (v < 0 || v > 100) {
|
|
2531
|
+
throw new Error(`[Code Mode Protocol Error] VAR[${i}] = ${v} is out of range. Values must be 0-100.`);
|
|
2532
|
+
}
|
|
2533
|
+
result.push(v);
|
|
2534
|
+
}
|
|
2535
|
+
while (result.length < 10) {
|
|
2536
|
+
result.push(0);
|
|
2537
|
+
}
|
|
2538
|
+
return result;
|
|
2539
|
+
}
|
|
2540
|
+
function validateInput(input) {
|
|
2541
|
+
if (!input.source || typeof input.source !== "string") {
|
|
2542
|
+
throw new Error("[Code Mode Protocol Error] source is required and must be a string");
|
|
2543
|
+
}
|
|
2544
|
+
if (typeof input.width !== "number" || input.width <= 0) {
|
|
2545
|
+
throw new Error("[Code Mode Protocol Error] width must be a positive number");
|
|
2546
|
+
}
|
|
2547
|
+
if (typeof input.height !== "number" || input.height <= 0) {
|
|
2548
|
+
throw new Error("[Code Mode Protocol Error] height must be a positive number");
|
|
2549
|
+
}
|
|
2550
|
+
if (typeof input.seed !== "number") {
|
|
2551
|
+
throw new Error("[Code Mode Protocol Error] seed is required and must be a number");
|
|
2552
|
+
}
|
|
2553
|
+
if (input.mode !== "static" && input.mode !== "loop") {
|
|
2554
|
+
throw new Error('[Code Mode Protocol Error] mode must be "static" or "loop"');
|
|
2555
|
+
}
|
|
2556
|
+
if (input.mode === "loop") {
|
|
2557
|
+
if (typeof input.totalFrames !== "number" || input.totalFrames <= 0) {
|
|
2558
|
+
throw new Error("[Code Mode Protocol Error] totalFrames is required for loop mode and must be a positive number");
|
|
2559
|
+
}
|
|
2560
|
+
}
|
|
2561
|
+
const forbiddenPatterns = [
|
|
2562
|
+
// Async timing (breaks determinism)
|
|
2563
|
+
{ pattern: /setTimeout\s*\(/, name: "setTimeout" },
|
|
2564
|
+
{ pattern: /setInterval\s*\(/, name: "setInterval" },
|
|
2565
|
+
{ pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" },
|
|
2566
|
+
// Time-based entropy (breaks determinism)
|
|
2567
|
+
{ pattern: /Date\.now\s*\(/, name: "Date.now() \u2014 use time variable instead" },
|
|
2568
|
+
{ pattern: /new\s+Date\s*\(/, name: "new Date() \u2014 use time variable instead" },
|
|
2569
|
+
// Unseeded random (use random() instead)
|
|
2570
|
+
{ pattern: /Math\.random\s*\(/, name: "Math.random() \u2014 use random() instead (seeded)" },
|
|
2571
|
+
// External IO (breaks determinism)
|
|
2572
|
+
{ pattern: /fetch\s*\(/, name: "fetch() \u2014 external IO forbidden" },
|
|
2573
|
+
{ pattern: /XMLHttpRequest/, name: "XMLHttpRequest \u2014 external IO forbidden" },
|
|
2574
|
+
// Canvas is pre-initialized
|
|
2575
|
+
{ pattern: /createCanvas\s*\(/, name: "createCanvas() \u2014 canvas is pre-initialized" },
|
|
2576
|
+
// DOM manipulation forbidden
|
|
2577
|
+
{ pattern: /document\./, name: "DOM access \u2014 document.* forbidden" },
|
|
2578
|
+
{ pattern: /window\./, name: "DOM access \u2014 window.* forbidden" },
|
|
2579
|
+
// External imports forbidden
|
|
2580
|
+
{ pattern: /\bimport\s+/, name: "import \u2014 external imports forbidden" },
|
|
2581
|
+
{ pattern: /\brequire\s*\(/, name: "require() \u2014 external imports forbidden" }
|
|
2582
|
+
];
|
|
2583
|
+
for (const { pattern, name } of forbiddenPatterns) {
|
|
2584
|
+
if (pattern.test(input.source)) {
|
|
2585
|
+
throw new Error(`[Code Mode Protocol Error] Forbidden pattern: ${name}`);
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
if (input.mode === "loop") {
|
|
2589
|
+
if (!/function\s+draw\s*\(\s*\)/.test(input.source)) {
|
|
2590
|
+
throw new Error("[Code Mode Protocol Error] Loop mode requires a draw() function");
|
|
2591
|
+
}
|
|
2592
|
+
if (/noLoop\s*\(\s*\)/.test(input.source)) {
|
|
2593
|
+
throw new Error("[Code Mode Protocol Error] noLoop() is forbidden in Loop mode");
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
function createMetadata(input, vars) {
|
|
2598
|
+
return {
|
|
2599
|
+
...PROTOCOL_IDENTITY,
|
|
2600
|
+
seed: input.seed,
|
|
2601
|
+
vars,
|
|
2602
|
+
width: input.width,
|
|
2603
|
+
height: input.height,
|
|
2604
|
+
mode: input.mode,
|
|
2605
|
+
...input.mode === "loop" && input.totalFrames ? { totalFrames: input.totalFrames } : {}
|
|
2606
|
+
};
|
|
2607
|
+
}
|
|
2608
|
+
async function executeStatic(input, vars) {
|
|
2609
|
+
console.log("[CodeMode] Rendered via @nexart/codemode-sdk (Protocol v1.2.0)");
|
|
2610
|
+
console.log("[CodeMode] Execution: Static mode \u2014 delegating to static-engine");
|
|
2611
|
+
return new Promise((resolve, reject) => {
|
|
2612
|
+
runStaticMode(
|
|
2613
|
+
{
|
|
2614
|
+
width: input.width,
|
|
2615
|
+
height: input.height
|
|
2616
|
+
},
|
|
2617
|
+
{
|
|
2618
|
+
code: input.source,
|
|
2619
|
+
seed: input.seed,
|
|
2620
|
+
vars,
|
|
2621
|
+
onComplete: (result) => {
|
|
2622
|
+
resolve({
|
|
2623
|
+
image: "blob" in result ? result.blob : void 0,
|
|
2624
|
+
frames: "imageData" in result ? [result.imageData] : void 0,
|
|
2625
|
+
metadata: createMetadata(input, vars)
|
|
2626
|
+
});
|
|
2627
|
+
},
|
|
2628
|
+
onError: (error) => {
|
|
2629
|
+
reject(error);
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
);
|
|
2633
|
+
});
|
|
2634
|
+
}
|
|
2635
|
+
async function executeLoop(input, vars) {
|
|
2636
|
+
console.log("[CodeMode] Rendered via @nexart/codemode-sdk (Protocol v1.2.0)");
|
|
2637
|
+
console.log(`[CodeMode] Execution: Loop mode \u2014 delegating to loop-engine (${input.totalFrames} frames)`);
|
|
2638
|
+
const fps = DEFAULT_CONFIG.fps;
|
|
2639
|
+
const duration = (input.totalFrames || 60) / fps;
|
|
2640
|
+
return new Promise((resolve, reject) => {
|
|
2641
|
+
runLoopMode(
|
|
2642
|
+
{
|
|
2643
|
+
width: input.width,
|
|
2644
|
+
height: input.height,
|
|
2645
|
+
duration,
|
|
2646
|
+
fps
|
|
2647
|
+
},
|
|
2648
|
+
{
|
|
2649
|
+
code: input.source,
|
|
2650
|
+
seed: input.seed,
|
|
2651
|
+
vars,
|
|
2652
|
+
onComplete: (result) => {
|
|
2653
|
+
resolve({
|
|
2654
|
+
video: "blob" in result && result.type === "video" ? result.blob : void 0,
|
|
2655
|
+
metadata: createMetadata(input, vars)
|
|
2656
|
+
});
|
|
2657
|
+
},
|
|
2658
|
+
onError: (error) => {
|
|
2659
|
+
reject(error);
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
);
|
|
2663
|
+
});
|
|
2664
|
+
}
|
|
2665
|
+
async function executeCodeMode(input) {
|
|
2666
|
+
validateInput(input);
|
|
2667
|
+
const vars = normalizeVars(input.vars);
|
|
2668
|
+
console.log("[CodeMode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
2669
|
+
console.log("[CodeMode] Protocol v1.2.0 \u2014 Phase 3 \u2014 HARD Enforcement");
|
|
2670
|
+
console.log(`[CodeMode] Mode: ${input.mode}`);
|
|
2671
|
+
console.log(`[CodeMode] Seed: ${input.seed}`);
|
|
2672
|
+
console.log(`[CodeMode] VAR: [${vars.join(", ")}]`);
|
|
2673
|
+
console.log("[CodeMode] \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
2674
|
+
if (input.mode === "static") {
|
|
2675
|
+
return executeStatic(input, vars);
|
|
2676
|
+
} else {
|
|
2677
|
+
return executeLoop(input, vars);
|
|
2678
|
+
}
|
|
2679
|
+
}
|
|
2680
|
+
function validateCodeModeSource(source, mode) {
|
|
2681
|
+
const errors = [];
|
|
2682
|
+
const forbiddenPatterns = [
|
|
2683
|
+
{ pattern: /setTimeout\s*\(/, name: "setTimeout" },
|
|
2684
|
+
{ pattern: /setInterval\s*\(/, name: "setInterval" },
|
|
2685
|
+
{ pattern: /requestAnimationFrame\s*\(/, name: "requestAnimationFrame" },
|
|
2686
|
+
{ pattern: /Date\.now\s*\(/, name: "Date.now() \u2014 use time variable instead" },
|
|
2687
|
+
{ pattern: /new\s+Date\s*\(/, name: "new Date() \u2014 use time variable instead" },
|
|
2688
|
+
{ pattern: /Math\.random\s*\(/, name: "Math.random() \u2014 use random() instead (seeded)" },
|
|
2689
|
+
{ pattern: /fetch\s*\(/, name: "fetch() \u2014 external IO forbidden" },
|
|
2690
|
+
{ pattern: /XMLHttpRequest/, name: "XMLHttpRequest \u2014 external IO forbidden" },
|
|
2691
|
+
{ pattern: /createCanvas\s*\(/, name: "createCanvas() \u2014 canvas is pre-initialized" },
|
|
2692
|
+
{ pattern: /document\./, name: "DOM access \u2014 document.* forbidden" },
|
|
2693
|
+
{ pattern: /window\./, name: "DOM access \u2014 window.* forbidden" },
|
|
2694
|
+
{ pattern: /\bimport\s+/, name: "import \u2014 external imports forbidden" },
|
|
2695
|
+
{ pattern: /\brequire\s*\(/, name: "require() \u2014 external imports forbidden" }
|
|
2696
|
+
];
|
|
2697
|
+
for (const { pattern, name } of forbiddenPatterns) {
|
|
2698
|
+
if (pattern.test(source)) {
|
|
2699
|
+
errors.push(`Forbidden pattern: ${name}`);
|
|
2700
|
+
}
|
|
2701
|
+
}
|
|
2702
|
+
if (mode === "loop") {
|
|
2703
|
+
if (!/function\s+draw\s*\(\s*\)/.test(source)) {
|
|
2704
|
+
errors.push("Loop mode requires a draw() function");
|
|
2705
|
+
}
|
|
2706
|
+
if (/noLoop\s*\(\s*\)/.test(source)) {
|
|
2707
|
+
errors.push("noLoop() is forbidden in Loop mode");
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
return {
|
|
2711
|
+
valid: errors.length === 0,
|
|
2712
|
+
errors
|
|
2713
|
+
};
|
|
2714
|
+
}
|
|
2715
|
+
|
|
2716
|
+
// engine.ts
|
|
2717
|
+
function createEngine(config) {
|
|
2718
|
+
const resolvedConfig = {
|
|
2719
|
+
mode: config.mode,
|
|
2720
|
+
width: config.width ?? DEFAULT_CONFIG.width,
|
|
2721
|
+
height: config.height ?? DEFAULT_CONFIG.height,
|
|
2722
|
+
duration: config.duration ?? DEFAULT_CONFIG.duration,
|
|
2723
|
+
fps: config.fps ?? DEFAULT_CONFIG.fps
|
|
2724
|
+
};
|
|
2725
|
+
let isRunning = false;
|
|
2726
|
+
const run = async (options) => {
|
|
2727
|
+
if (isRunning) {
|
|
2728
|
+
throw new Error("Engine is already running. Call stop() first.");
|
|
2729
|
+
}
|
|
2730
|
+
isRunning = true;
|
|
2731
|
+
try {
|
|
2732
|
+
if (resolvedConfig.mode === "static") {
|
|
2733
|
+
await runStaticMode(resolvedConfig, options);
|
|
2734
|
+
} else if (resolvedConfig.mode === "loop") {
|
|
2735
|
+
await runLoopMode(resolvedConfig, options);
|
|
2736
|
+
} else {
|
|
2737
|
+
throw new Error(`Unknown mode: ${resolvedConfig.mode}`);
|
|
2738
|
+
}
|
|
2739
|
+
} finally {
|
|
2740
|
+
isRunning = false;
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2743
|
+
const stop = () => {
|
|
2744
|
+
if (resolvedConfig.mode === "loop") {
|
|
2745
|
+
cancelLoopMode();
|
|
2746
|
+
}
|
|
2747
|
+
isRunning = false;
|
|
2748
|
+
};
|
|
2749
|
+
const getConfig = () => {
|
|
2750
|
+
return { ...resolvedConfig };
|
|
2751
|
+
};
|
|
2752
|
+
return {
|
|
2753
|
+
run,
|
|
2754
|
+
stop,
|
|
2755
|
+
getConfig
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
// runtime.ts
|
|
2760
|
+
var RUNTIME_VERSION = SDK_VERSION;
|
|
2761
|
+
function hashSeed(seed) {
|
|
2762
|
+
if (typeof seed === "number") {
|
|
2763
|
+
return Math.floor(seed) >>> 0;
|
|
2764
|
+
}
|
|
2765
|
+
let hash = 0;
|
|
2766
|
+
for (let i = 0; i < seed.length; i++) {
|
|
2767
|
+
const char = seed.charCodeAt(i);
|
|
2768
|
+
hash = (hash << 5) - hash + char;
|
|
2769
|
+
hash = hash >>> 0;
|
|
2770
|
+
}
|
|
2771
|
+
return hash || 1;
|
|
2772
|
+
}
|
|
2773
|
+
function createSeededRNG2(seed) {
|
|
2774
|
+
let a = seed >>> 0;
|
|
2775
|
+
return () => {
|
|
2776
|
+
a += 1831565813;
|
|
2777
|
+
let t = Math.imul(a ^ a >>> 15, a | 1);
|
|
2778
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
2779
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
2780
|
+
};
|
|
2781
|
+
}
|
|
2782
|
+
function createSeededNoise2(seed) {
|
|
2783
|
+
const permutation = [];
|
|
2784
|
+
const rng = createSeededRNG2(seed);
|
|
2785
|
+
for (let i = 0; i < 256; i++) {
|
|
2786
|
+
permutation[i] = i;
|
|
2787
|
+
}
|
|
2788
|
+
for (let i = 255; i > 0; i--) {
|
|
2789
|
+
const j = Math.floor(rng() * (i + 1));
|
|
2790
|
+
[permutation[i], permutation[j]] = [permutation[j], permutation[i]];
|
|
2791
|
+
}
|
|
2792
|
+
for (let i = 0; i < 256; i++) {
|
|
2793
|
+
permutation[256 + i] = permutation[i];
|
|
2794
|
+
}
|
|
2795
|
+
const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
|
|
2796
|
+
const lerp = (a, b, t) => a + t * (b - a);
|
|
2797
|
+
const grad = (hash, x, y, z) => {
|
|
2798
|
+
const h = hash & 15;
|
|
2799
|
+
const u = h < 8 ? x : y;
|
|
2800
|
+
const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
|
|
2801
|
+
return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
|
|
2802
|
+
};
|
|
2803
|
+
return (x, y = 0, z = 0) => {
|
|
2804
|
+
const X = Math.floor(x) & 255;
|
|
2805
|
+
const Y = Math.floor(y) & 255;
|
|
2806
|
+
const Z = Math.floor(z) & 255;
|
|
2807
|
+
x -= Math.floor(x);
|
|
2808
|
+
y -= Math.floor(y);
|
|
2809
|
+
z -= Math.floor(z);
|
|
2810
|
+
const u = fade(x);
|
|
2811
|
+
const v = fade(y);
|
|
2812
|
+
const w = fade(z);
|
|
2813
|
+
const A = permutation[X] + Y;
|
|
2814
|
+
const AA = permutation[A] + Z;
|
|
2815
|
+
const AB = permutation[A + 1] + Z;
|
|
2816
|
+
const B = permutation[X + 1] + Y;
|
|
2817
|
+
const BA = permutation[B] + Z;
|
|
2818
|
+
const BB = permutation[B + 1] + Z;
|
|
2819
|
+
return (lerp(
|
|
2820
|
+
lerp(
|
|
2821
|
+
lerp(grad(permutation[AA], x, y, z), grad(permutation[BA], x - 1, y, z), u),
|
|
2822
|
+
lerp(grad(permutation[AB], x, y - 1, z), grad(permutation[BB], x - 1, y - 1, z), u),
|
|
2823
|
+
v
|
|
2824
|
+
),
|
|
2825
|
+
lerp(
|
|
2826
|
+
lerp(grad(permutation[AA + 1], x, y, z - 1), grad(permutation[BA + 1], x - 1, y, z - 1), u),
|
|
2827
|
+
lerp(grad(permutation[AB + 1], x, y - 1, z - 1), grad(permutation[BB + 1], x - 1, y - 1, z - 1), u),
|
|
2828
|
+
v
|
|
2829
|
+
),
|
|
2830
|
+
w
|
|
2831
|
+
) + 1) / 2;
|
|
2832
|
+
};
|
|
2833
|
+
}
|
|
2834
|
+
function stableStringify(obj) {
|
|
2835
|
+
if (obj === null) return "null";
|
|
2836
|
+
if (obj === void 0) return "undefined";
|
|
2837
|
+
if (typeof obj !== "object") return JSON.stringify(obj);
|
|
2838
|
+
if (Array.isArray(obj)) {
|
|
2839
|
+
return "[" + obj.map(stableStringify).join(",") + "]";
|
|
2840
|
+
}
|
|
2841
|
+
const keys = Object.keys(obj).sort();
|
|
2842
|
+
const pairs = keys.map((k) => `${JSON.stringify(k)}:${stableStringify(obj[k])}`);
|
|
2843
|
+
return "{" + pairs.join(",") + "}";
|
|
2844
|
+
}
|
|
2845
|
+
function fnv1aHash(str) {
|
|
2846
|
+
let hash = 2166136261;
|
|
2847
|
+
for (let i = 0; i < str.length; i++) {
|
|
2848
|
+
hash ^= str.charCodeAt(i);
|
|
2849
|
+
hash = Math.imul(hash, 16777619);
|
|
2850
|
+
}
|
|
2851
|
+
const h1 = (hash >>> 0).toString(16).padStart(8, "0");
|
|
2852
|
+
let hash2 = 2166136261;
|
|
2853
|
+
for (let i = str.length - 1; i >= 0; i--) {
|
|
2854
|
+
hash2 ^= str.charCodeAt(i);
|
|
2855
|
+
hash2 = Math.imul(hash2, 16777619);
|
|
2856
|
+
}
|
|
2857
|
+
const h2 = (hash2 >>> 0).toString(16).padStart(8, "0");
|
|
2858
|
+
return h1 + h2;
|
|
2859
|
+
}
|
|
2860
|
+
function createRuntime(options) {
|
|
2861
|
+
const numericSeed = hashSeed(options.seed);
|
|
2862
|
+
const vars = options.vars ?? [];
|
|
2863
|
+
const mode = options.mode ?? "static";
|
|
2864
|
+
const strict = options.strict ?? false;
|
|
2865
|
+
const metadata = options.metadata;
|
|
2866
|
+
if (vars.length > 10) {
|
|
2867
|
+
throw new Error(`[NexArt Runtime] vars array must have 0-10 elements, got ${vars.length}`);
|
|
2868
|
+
}
|
|
2869
|
+
for (let i = 0; i < vars.length; i++) {
|
|
2870
|
+
if (typeof vars[i] !== "number" || !Number.isFinite(vars[i])) {
|
|
2871
|
+
throw new Error(`[NexArt Runtime] vars[${i}] must be a finite number`);
|
|
2872
|
+
}
|
|
2873
|
+
if (vars[i] < 0 || vars[i] > 100) {
|
|
2874
|
+
throw new Error(`[NexArt Runtime] vars[${i}] must be in range 0-100, got ${vars[i]}`);
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
2877
|
+
const paddedVars = [...vars];
|
|
2878
|
+
while (paddedVars.length < 10) {
|
|
2879
|
+
paddedVars.push(0);
|
|
2880
|
+
}
|
|
2881
|
+
const rng = createSeededRNG2(numericSeed);
|
|
2882
|
+
const noiseFunc = createSeededNoise2(numericSeed);
|
|
2883
|
+
const state = {
|
|
2884
|
+
sdkVersion: RUNTIME_VERSION,
|
|
2885
|
+
seed: numericSeed,
|
|
2886
|
+
vars: paddedVars,
|
|
2887
|
+
mode,
|
|
2888
|
+
...metadata !== void 0 && { metadata }
|
|
2889
|
+
};
|
|
2890
|
+
function random() {
|
|
2891
|
+
return rng();
|
|
2892
|
+
}
|
|
2893
|
+
function randomInt(min, max) {
|
|
2894
|
+
const range = max - min + 1;
|
|
2895
|
+
return Math.floor(rng() * range) + min;
|
|
2896
|
+
}
|
|
2897
|
+
function randomRange(min, max) {
|
|
2898
|
+
return rng() * (max - min) + min;
|
|
2899
|
+
}
|
|
2900
|
+
function noise(x, y, z) {
|
|
2901
|
+
return noiseFunc(x, y ?? 0, z ?? 0);
|
|
2902
|
+
}
|
|
2903
|
+
function run(fn) {
|
|
2904
|
+
if (!strict) {
|
|
2905
|
+
return fn();
|
|
2906
|
+
}
|
|
2907
|
+
const originalMathRandom = Math.random;
|
|
2908
|
+
const originalDateNow = Date.now;
|
|
2909
|
+
const hasPerformance = typeof performance !== "undefined" && performance !== null;
|
|
2910
|
+
const originalPerformanceNow = hasPerformance ? performance.now : void 0;
|
|
2911
|
+
const throwMathRandom = () => {
|
|
2912
|
+
throw new Error("NEXART_STRICT: Non-deterministic API used: Math.random. Use runtime.random() instead.");
|
|
2913
|
+
};
|
|
2914
|
+
const throwDateNow = () => {
|
|
2915
|
+
throw new Error("NEXART_STRICT: Non-deterministic API used: Date.now. Pass time as an input or use deterministic counters.");
|
|
2916
|
+
};
|
|
2917
|
+
const throwPerformanceNow = () => {
|
|
2918
|
+
throw new Error("NEXART_STRICT: Non-deterministic API used: performance.now.");
|
|
2919
|
+
};
|
|
2920
|
+
try {
|
|
2921
|
+
Math.random = throwMathRandom;
|
|
2922
|
+
Date.now = throwDateNow;
|
|
2923
|
+
if (hasPerformance && originalPerformanceNow) {
|
|
2924
|
+
performance.now = throwPerformanceNow;
|
|
2925
|
+
}
|
|
2926
|
+
return fn();
|
|
2927
|
+
} finally {
|
|
2928
|
+
Math.random = originalMathRandom;
|
|
2929
|
+
Date.now = originalDateNow;
|
|
2930
|
+
if (hasPerformance && originalPerformanceNow) {
|
|
2931
|
+
performance.now = originalPerformanceNow;
|
|
2932
|
+
}
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
function digest() {
|
|
2936
|
+
const input = stableStringify({
|
|
2937
|
+
sdkVersion: RUNTIME_VERSION,
|
|
2938
|
+
seed: numericSeed,
|
|
2939
|
+
vars: paddedVars,
|
|
2940
|
+
mode,
|
|
2941
|
+
...metadata !== void 0 && { metadata }
|
|
2942
|
+
});
|
|
2943
|
+
return fnv1aHash(input);
|
|
2944
|
+
}
|
|
2945
|
+
function getState() {
|
|
2946
|
+
return { ...state, vars: [...state.vars] };
|
|
2947
|
+
}
|
|
2948
|
+
function getSeed() {
|
|
2949
|
+
return numericSeed;
|
|
2950
|
+
}
|
|
2951
|
+
return Object.freeze({
|
|
2952
|
+
random,
|
|
2953
|
+
randomInt,
|
|
2954
|
+
randomRange,
|
|
2955
|
+
noise,
|
|
2956
|
+
run,
|
|
2957
|
+
digest,
|
|
2958
|
+
getState,
|
|
2959
|
+
getSeed,
|
|
2960
|
+
strict
|
|
2961
|
+
});
|
|
2962
|
+
}
|
|
2963
|
+
var NexArtRuntime = {
|
|
2964
|
+
create: createRuntime,
|
|
2965
|
+
VERSION: RUNTIME_VERSION
|
|
2966
|
+
};
|
|
2967
|
+
|
|
2968
|
+
// ../../shared/soundSnapshot.ts
|
|
2969
|
+
function clampPercent(value) {
|
|
2970
|
+
return Math.max(0, Math.min(100, value));
|
|
2971
|
+
}
|
|
2972
|
+
function toPercent(value) {
|
|
2973
|
+
return clampPercent(value * 100);
|
|
2974
|
+
}
|
|
2975
|
+
function createSoundSnapshot(sound, frames, windowMs = 800, frameIndex, totalFrames) {
|
|
2976
|
+
const framesArr = frames || sound.frames || [];
|
|
2977
|
+
const bands = {
|
|
2978
|
+
bass: sound.bands?.bass ?? 0.3,
|
|
2979
|
+
lowMid: sound.bands?.lowMid ?? 0.25,
|
|
2980
|
+
highMid: sound.bands?.highMid ?? 0.25,
|
|
2981
|
+
treble: sound.bands?.treble ?? 0.2
|
|
2982
|
+
};
|
|
2983
|
+
const now = framesArr.length > 0 ? framesArr[framesArr.length - 1].t1 * 1e3 : 0;
|
|
2984
|
+
const windowStart = now - windowMs;
|
|
2985
|
+
const recentFrames = framesArr.filter((f) => f.t1 * 1e3 >= windowStart);
|
|
2986
|
+
const useFrames = recentFrames.length > 0 ? recentFrames : framesArr;
|
|
2987
|
+
const getFrameAvg = (key, def) => {
|
|
2988
|
+
if (useFrames.length === 0) return def;
|
|
2989
|
+
const values = useFrames.map((f) => f[key] ?? def);
|
|
2990
|
+
return values.reduce((a, b) => a + b, 0) / values.length;
|
|
2991
|
+
};
|
|
2992
|
+
const getFrameSpan = (key, def) => {
|
|
2993
|
+
if (useFrames.length < 2) return 0;
|
|
2994
|
+
const values = useFrames.map((f) => f[key] ?? def);
|
|
2995
|
+
return Math.max(...values) - Math.min(...values);
|
|
2996
|
+
};
|
|
2997
|
+
const rmsNow = getFrameAvg("rms", sound.rms ?? 0);
|
|
2998
|
+
const ampNow = getFrameAvg("amplitude", sound.rms ?? 0);
|
|
2999
|
+
const attNow = getFrameAvg("attack", 0.5);
|
|
3000
|
+
const harmNow = getFrameAvg("harmonicity", 0.5);
|
|
3001
|
+
const aggrNow = getFrameAvg("aggression", 0.5);
|
|
3002
|
+
const rhNow = getFrameAvg("rhythmicity", 0.4);
|
|
3003
|
+
const silNow = getFrameAvg("silence", 0);
|
|
3004
|
+
const dynNow = getFrameSpan("rms", sound.rms ?? 0);
|
|
3005
|
+
const centNow = getFrameAvg("centroid", sound.centroid ?? 0.5);
|
|
3006
|
+
const amplifiedRms = Math.pow(rmsNow, 0.33);
|
|
3007
|
+
const amplifiedAmp = Math.pow(ampNow, 0.4);
|
|
3008
|
+
const t = framesArr.length > 0 ? 1 : 0;
|
|
3009
|
+
return {
|
|
3010
|
+
volume: toPercent(amplifiedRms),
|
|
3011
|
+
amplitude: toPercent(amplifiedAmp),
|
|
3012
|
+
dynamicRange: toPercent(dynNow),
|
|
3013
|
+
brightness: toPercent(centNow),
|
|
3014
|
+
bass: toPercent(bands.bass),
|
|
3015
|
+
mid: toPercent((bands.lowMid + bands.highMid) / 2),
|
|
3016
|
+
treble: toPercent(bands.treble),
|
|
3017
|
+
harmonicity: toPercent(harmNow),
|
|
3018
|
+
aggression: toPercent(aggrNow),
|
|
3019
|
+
attack: toPercent(attNow),
|
|
3020
|
+
rhythmicity: toPercent(rhNow),
|
|
3021
|
+
silence: toPercent(silNow),
|
|
3022
|
+
hue: toPercent(centNow * 0.75 + aggrNow * 0.25),
|
|
3023
|
+
chroma: toPercent(Math.min(1, Math.sqrt((bands.bass + bands.treble) / 2))),
|
|
3024
|
+
length: Math.min(100, (sound.durationSec ?? 0) * 10),
|
|
3025
|
+
t,
|
|
3026
|
+
frame: frameIndex,
|
|
3027
|
+
totalFrames
|
|
3028
|
+
};
|
|
3029
|
+
}
|
|
3030
|
+
function freezeSoundSnapshot(snapshot) {
|
|
3031
|
+
return Object.freeze({ ...snapshot });
|
|
3032
|
+
}
|
|
3033
|
+
|
|
3034
|
+
// sound-bridge.ts
|
|
3035
|
+
function createSoundGlobals(snapshot) {
|
|
3036
|
+
return freezeSoundSnapshot(snapshot);
|
|
3037
|
+
}
|
|
3038
|
+
function injectSoundGlobals(runtime, snapshot) {
|
|
3039
|
+
const globals = createSoundGlobals(snapshot);
|
|
3040
|
+
const immutableS = new Proxy(globals, {
|
|
3041
|
+
set() {
|
|
3042
|
+
console.warn("[SoundBridge] S.* globals are read-only");
|
|
3043
|
+
return false;
|
|
3044
|
+
},
|
|
3045
|
+
deleteProperty() {
|
|
3046
|
+
console.warn("[SoundBridge] Cannot delete S.* properties");
|
|
3047
|
+
return false;
|
|
3048
|
+
},
|
|
3049
|
+
defineProperty() {
|
|
3050
|
+
console.warn("[SoundBridge] Cannot define new S.* properties");
|
|
3051
|
+
return false;
|
|
3052
|
+
}
|
|
3053
|
+
});
|
|
3054
|
+
return {
|
|
3055
|
+
...runtime,
|
|
3056
|
+
S: immutableS
|
|
3057
|
+
};
|
|
3058
|
+
}
|
|
3059
|
+
function hueToDegrees(hue) {
|
|
3060
|
+
return hue / 100 * 360;
|
|
3061
|
+
}
|
|
3062
|
+
function generateSoundPalette(snapshot, paletteSize = 6) {
|
|
3063
|
+
const mainHue = hueToDegrees(snapshot.hue);
|
|
3064
|
+
const hueShift = 20 + snapshot.harmonicity / 100 * 40;
|
|
3065
|
+
const sat = 60 + snapshot.treble / 100 * 30;
|
|
3066
|
+
const light = 70 + snapshot.bass / 100 * 25;
|
|
3067
|
+
const palette = [];
|
|
3068
|
+
for (let i = 0; i < paletteSize; i++) {
|
|
3069
|
+
const hue = (mainHue + i * hueShift) % 360;
|
|
3070
|
+
const satVar = sat - i % 2 * 10;
|
|
3071
|
+
const lightVar = light - i % 3 * 5;
|
|
3072
|
+
palette.push(`hsl(${hue}, ${satVar}%, ${lightVar}%)`);
|
|
3073
|
+
}
|
|
3074
|
+
return palette;
|
|
3075
|
+
}
|
|
3076
|
+
function inferGenreProfile(snapshot) {
|
|
3077
|
+
const energy = snapshot.volume > 60 || snapshot.aggression > 60 ? "high" : snapshot.volume > 30 ? "mid" : "low";
|
|
3078
|
+
const structure = snapshot.rhythmicity > 50 && snapshot.dynamicRange < 25 ? "geometric" : snapshot.aggression > 60 && snapshot.dynamicRange > 50 ? "chaotic" : "organic";
|
|
3079
|
+
const clarity = snapshot.brightness > 60 ? "sharp" : snapshot.brightness > 40 ? "clear" : "muddy";
|
|
3080
|
+
return { energy, structure, clarity };
|
|
3081
|
+
}
|
|
3082
|
+
|
|
3083
|
+
// soundart-engine.ts
|
|
3084
|
+
init_soundart_sketches();
|
|
3085
|
+
function applyTweaksToSnapshot(snapshot, tweaks) {
|
|
3086
|
+
if (!tweaks) return snapshot;
|
|
3087
|
+
const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
|
|
3088
|
+
const tweaked = {
|
|
3089
|
+
...snapshot,
|
|
3090
|
+
volume: snapshot.volume * (tweaks.volumeRangeFactor ?? 1),
|
|
3091
|
+
brightness: snapshot.brightness * (tweaks.centroidBrightnessFactor ?? 1),
|
|
3092
|
+
aggression: snapshot.aggression * (tweaks.aggressionPaletteFactor ?? 1),
|
|
3093
|
+
rhythmicity: snapshot.rhythmicity * (tweaks.rhythmPatternFactor ?? 1),
|
|
3094
|
+
harmonicity: snapshot.harmonicity * (tweaks.harmonySmoothnessFactor ?? 1),
|
|
3095
|
+
attack: snapshot.attack * (tweaks.attackSharpnessFactor ?? 1),
|
|
3096
|
+
dynamicRange: snapshot.dynamicRange * (tweaks.dynamicRangeVariationFactor ?? 1),
|
|
3097
|
+
bass: snapshot.bass * (tweaks.bassThicknessFactor ?? 1),
|
|
3098
|
+
treble: snapshot.treble * (tweaks.trebleSharpnessFactor ?? 1)
|
|
3099
|
+
};
|
|
3100
|
+
tweaked.volume = clamp(tweaked.volume, 0, 250);
|
|
3101
|
+
tweaked.brightness = clamp(tweaked.brightness, 0, 250);
|
|
3102
|
+
tweaked.aggression = clamp(tweaked.aggression, 0, 250);
|
|
3103
|
+
tweaked.rhythmicity = clamp(tweaked.rhythmicity, 0, 250);
|
|
3104
|
+
tweaked.harmonicity = clamp(tweaked.harmonicity, 0, 250);
|
|
3105
|
+
tweaked.attack = clamp(tweaked.attack, 0, 250);
|
|
3106
|
+
tweaked.dynamicRange = clamp(tweaked.dynamicRange, 0, 250);
|
|
3107
|
+
tweaked.bass = clamp(tweaked.bass, 0, 250);
|
|
3108
|
+
tweaked.treble = clamp(tweaked.treble, 0, 250);
|
|
3109
|
+
return tweaked;
|
|
3110
|
+
}
|
|
3111
|
+
async function renderSoundArtViaCodeMode(options) {
|
|
3112
|
+
const { style, sound, canvas, config, onProgress } = options;
|
|
3113
|
+
onProgress?.(0.1);
|
|
3114
|
+
const sketchCode = getSoundArtSketch(style);
|
|
3115
|
+
if (!sketchCode) {
|
|
3116
|
+
throw new Error(`SoundArt style "${style}" has not been converted to Code Mode yet`);
|
|
3117
|
+
}
|
|
3118
|
+
onProgress?.(0.2);
|
|
3119
|
+
const baseSnapshot = createSoundSnapshot(sound, sound.frames);
|
|
3120
|
+
const snapshot = applyTweaksToSnapshot(baseSnapshot, config.tweakParams);
|
|
3121
|
+
onProgress?.(0.3);
|
|
3122
|
+
const deriveSeedFromSound = (sound2) => {
|
|
3123
|
+
const hashInput = sound2.rms * 1e3 + sound2.centroid * 100 + sound2.durationSec * 1e4 + (sound2.bands?.bass ?? 0) * 500 + (sound2.bands?.treble ?? 0) * 700;
|
|
3124
|
+
return Math.abs(Math.floor(hashInput * 2147483647) % 2147483647) || 123456;
|
|
3125
|
+
};
|
|
3126
|
+
const runtimeConfig = {
|
|
3127
|
+
seed: config.seed ?? deriveSeedFromSound(sound)
|
|
3128
|
+
};
|
|
3129
|
+
canvas.width = config.width;
|
|
3130
|
+
canvas.height = config.height;
|
|
3131
|
+
const baseRuntime = createP5Runtime(canvas, config.width, config.height, runtimeConfig);
|
|
3132
|
+
const runtime = injectSoundGlobals(baseRuntime, snapshot);
|
|
3133
|
+
onProgress?.(0.4);
|
|
3134
|
+
const globals = {
|
|
3135
|
+
...runtime,
|
|
3136
|
+
width: config.width,
|
|
3137
|
+
height: config.height,
|
|
3138
|
+
backgroundMode: config.backgroundMode ?? "rgb",
|
|
3139
|
+
palette: generateSoundPalette(snapshot),
|
|
3140
|
+
genre: inferGenreProfile(snapshot)
|
|
3141
|
+
};
|
|
3142
|
+
onProgress?.(0.5);
|
|
3143
|
+
try {
|
|
3144
|
+
executeSketch(sketchCode, globals);
|
|
3145
|
+
onProgress?.(0.9);
|
|
3146
|
+
} catch (error) {
|
|
3147
|
+
console.error(`[SoundArtEngine] Error executing style "${style}":`, error);
|
|
3148
|
+
throw new Error(`Style "${style}" execution failed: ${error}`);
|
|
3149
|
+
}
|
|
3150
|
+
onProgress?.(1);
|
|
3151
|
+
const metadata = {
|
|
3152
|
+
mode: "SoundArt",
|
|
3153
|
+
style,
|
|
3154
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3155
|
+
soundFeatures: {
|
|
3156
|
+
duration: sound.durationSec,
|
|
3157
|
+
amplitude: sound.rms,
|
|
3158
|
+
frequency: sound.centroid,
|
|
3159
|
+
bass: sound.bands?.bass ?? 0,
|
|
3160
|
+
treble: sound.bands?.treble ?? 0,
|
|
3161
|
+
aggression: snapshot.aggression
|
|
3162
|
+
},
|
|
3163
|
+
effects: {
|
|
3164
|
+
timeFilter: false,
|
|
3165
|
+
backgroundMode: config.backgroundMode ?? "rgb"
|
|
3166
|
+
},
|
|
3167
|
+
generationParams: {
|
|
3168
|
+
seed: runtimeConfig.seed,
|
|
3169
|
+
canvasSize: { width: config.width, height: config.height }
|
|
3170
|
+
}
|
|
3171
|
+
};
|
|
3172
|
+
return {
|
|
3173
|
+
style,
|
|
3174
|
+
mode: "soundart",
|
|
3175
|
+
snapshot,
|
|
3176
|
+
metadata
|
|
3177
|
+
};
|
|
3178
|
+
}
|
|
3179
|
+
function executeSketch(code, globals) {
|
|
3180
|
+
const globalNames = Object.keys(globals);
|
|
3181
|
+
const globalValues = Object.values(globals);
|
|
3182
|
+
const wrappedCode = `
|
|
3183
|
+
${code}
|
|
3184
|
+
|
|
3185
|
+
// Execute setup
|
|
3186
|
+
if (typeof setup === 'function') {
|
|
3187
|
+
setup();
|
|
3188
|
+
}
|
|
3189
|
+
|
|
3190
|
+
// For static mode, we don't call draw()
|
|
3191
|
+
// Loop mode would call draw() in a frame loop
|
|
3192
|
+
`;
|
|
3193
|
+
const sketchFunction = new Function(...globalNames, wrappedCode);
|
|
3194
|
+
sketchFunction(...globalValues);
|
|
3195
|
+
}
|
|
3196
|
+
function canRenderViaCodeMode(style) {
|
|
3197
|
+
return getSoundArtSketch(style) !== void 0;
|
|
3198
|
+
}
|
|
3199
|
+
function getCodeModeAvailableStyles() {
|
|
3200
|
+
const { getAvailableSoundArtSketches: getAvailableSoundArtSketches2 } = (init_soundart_sketches(), __toCommonJS(soundart_sketches_exports));
|
|
3201
|
+
return getAvailableSoundArtSketches2();
|
|
3202
|
+
}
|
|
3203
|
+
|
|
3204
|
+
// entry/browser.ts
|
|
3205
|
+
var SDK_VERSION2 = SDK_VERSION;
|
|
3206
|
+
var SDK_NAME = "@nexart/codemode-sdk";
|
|
3207
|
+
|
|
3208
|
+
// entry/node.ts
|
|
3209
|
+
var SDK_ENTRY = "node";
|
|
3210
|
+
|
|
3211
|
+
exports.CODE_MODE_ENFORCEMENT = CODE_MODE_ENFORCEMENT;
|
|
3212
|
+
exports.CODE_MODE_PROTOCOL_PHASE = CODE_MODE_PROTOCOL_PHASE;
|
|
3213
|
+
exports.CODE_MODE_PROTOCOL_VERSION = CODE_MODE_PROTOCOL_VERSION;
|
|
3214
|
+
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
3215
|
+
exports.DEFAULT_VARS = DEFAULT_VARS;
|
|
3216
|
+
exports.FORBIDDEN_APIS = FORBIDDEN_APIS;
|
|
3217
|
+
exports.FORBIDDEN_API_NAMES = FORBIDDEN_API_NAMES;
|
|
3218
|
+
exports.NexArtRuntime = NexArtRuntime;
|
|
3219
|
+
exports.PROTOCOL_IDENTITY = PROTOCOL_IDENTITY;
|
|
3220
|
+
exports.RUNTIME_VERSION = RUNTIME_VERSION;
|
|
3221
|
+
exports.SDK_ENTRY = SDK_ENTRY;
|
|
3222
|
+
exports.SDK_NAME = SDK_NAME;
|
|
3223
|
+
exports.SDK_VERSION = SDK_VERSION2;
|
|
3224
|
+
exports.VAR_COUNT = VAR_COUNT;
|
|
3225
|
+
exports.VAR_MAX = VAR_MAX;
|
|
3226
|
+
exports.VAR_MIN = VAR_MIN;
|
|
3227
|
+
exports.buildSandboxContext = buildSandboxContext;
|
|
3228
|
+
exports.canRenderViaCodeMode = canRenderViaCodeMode;
|
|
3229
|
+
exports.cancelLoopMode = cancelLoopMode;
|
|
3230
|
+
exports.createEngine = createEngine;
|
|
3231
|
+
exports.createP5Runtime = createP5Runtime;
|
|
3232
|
+
exports.createProtocolVAR = createProtocolVAR;
|
|
3233
|
+
exports.createRuntime = createRuntime;
|
|
3234
|
+
exports.createSafeMath = createSafeMath;
|
|
3235
|
+
exports.createSandboxedExecutor = createSandboxedExecutor;
|
|
3236
|
+
exports.executeCodeMode = executeCodeMode;
|
|
3237
|
+
exports.executeSandboxed = executeSandboxed;
|
|
3238
|
+
exports.getCodeModeAvailableStyles = getCodeModeAvailableStyles;
|
|
3239
|
+
exports.injectProtocolVariables = injectProtocolVariables;
|
|
3240
|
+
exports.injectTimeVariables = injectTimeVariables;
|
|
3241
|
+
exports.registerBuilderManifest = registerBuilderManifest;
|
|
3242
|
+
exports.renderSoundArtViaCodeMode = renderSoundArtViaCodeMode;
|
|
3243
|
+
exports.runLoopMode = runLoopMode;
|
|
3244
|
+
exports.runStaticMode = runStaticMode;
|
|
3245
|
+
exports.validateCodeModeSource = validateCodeModeSource;
|