@nexart/codemode-sdk 1.1.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +121 -238
  2. package/dist/engine.d.ts +1 -1
  3. package/dist/engine.js +1 -1
  4. package/dist/index.d.ts +19 -27
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +19 -34
  7. package/dist/loop-engine.d.ts +1 -4
  8. package/dist/loop-engine.d.ts.map +1 -1
  9. package/dist/loop-engine.js +12 -17
  10. package/dist/p5-runtime.d.ts +4 -56
  11. package/dist/p5-runtime.d.ts.map +1 -1
  12. package/dist/p5-runtime.js +22 -348
  13. package/dist/static-engine.d.ts +1 -4
  14. package/dist/static-engine.d.ts.map +1 -1
  15. package/dist/static-engine.js +10 -14
  16. package/dist/types.d.ts +7 -7
  17. package/dist/types.d.ts.map +1 -1
  18. package/dist/types.js +3 -3
  19. package/package.json +17 -23
  20. package/CHANGELOG.md +0 -109
  21. package/CODE_MODE_PROTOCOL.md +0 -312
  22. package/dist/core-index.d.ts +0 -21
  23. package/dist/core-index.d.ts.map +0 -1
  24. package/dist/core-index.js +0 -26
  25. package/dist/execute.d.ts +0 -46
  26. package/dist/execute.d.ts.map +0 -1
  27. package/dist/execute.js +0 -268
  28. package/dist/noise-bridge.d.ts +0 -44
  29. package/dist/noise-bridge.d.ts.map +0 -1
  30. package/dist/noise-bridge.js +0 -68
  31. package/dist/noise-engine.d.ts +0 -74
  32. package/dist/noise-engine.d.ts.map +0 -1
  33. package/dist/noise-engine.js +0 -132
  34. package/dist/noise-sketches/fractalNoise.d.ts +0 -11
  35. package/dist/noise-sketches/fractalNoise.d.ts.map +0 -1
  36. package/dist/noise-sketches/fractalNoise.js +0 -121
  37. package/dist/noise-sketches/index.d.ts +0 -21
  38. package/dist/noise-sketches/index.d.ts.map +0 -1
  39. package/dist/noise-sketches/index.js +0 -28
  40. package/dist/sound-bridge.d.ts +0 -89
  41. package/dist/sound-bridge.d.ts.map +0 -1
  42. package/dist/sound-bridge.js +0 -128
  43. package/dist/soundart-engine.d.ts +0 -87
  44. package/dist/soundart-engine.d.ts.map +0 -1
  45. package/dist/soundart-engine.js +0 -173
  46. package/dist/soundart-sketches/chladniBloom.d.ts +0 -3
  47. package/dist/soundart-sketches/chladniBloom.d.ts.map +0 -1
  48. package/dist/soundart-sketches/chladniBloom.js +0 -53
  49. package/dist/soundart-sketches/dualVortex.d.ts +0 -3
  50. package/dist/soundart-sketches/dualVortex.d.ts.map +0 -1
  51. package/dist/soundart-sketches/dualVortex.js +0 -67
  52. package/dist/soundart-sketches/geometryIllusion.d.ts +0 -3
  53. package/dist/soundart-sketches/geometryIllusion.d.ts.map +0 -1
  54. package/dist/soundart-sketches/geometryIllusion.js +0 -89
  55. package/dist/soundart-sketches/index.d.ts +0 -39
  56. package/dist/soundart-sketches/index.d.ts.map +0 -1
  57. package/dist/soundart-sketches/index.js +0 -72
  58. package/dist/soundart-sketches/isoflow.d.ts +0 -3
  59. package/dist/soundart-sketches/isoflow.d.ts.map +0 -1
  60. package/dist/soundart-sketches/isoflow.js +0 -60
  61. package/dist/soundart-sketches/loomWeave.d.ts +0 -3
  62. package/dist/soundart-sketches/loomWeave.d.ts.map +0 -1
  63. package/dist/soundart-sketches/loomWeave.js +0 -59
  64. package/dist/soundart-sketches/noiseTerraces.d.ts +0 -3
  65. package/dist/soundart-sketches/noiseTerraces.d.ts.map +0 -1
  66. package/dist/soundart-sketches/noiseTerraces.js +0 -53
  67. package/dist/soundart-sketches/orb.d.ts +0 -3
  68. package/dist/soundart-sketches/orb.d.ts.map +0 -1
  69. package/dist/soundart-sketches/orb.js +0 -50
  70. package/dist/soundart-sketches/pixelGlyphs.d.ts +0 -3
  71. package/dist/soundart-sketches/pixelGlyphs.d.ts.map +0 -1
  72. package/dist/soundart-sketches/pixelGlyphs.js +0 -72
  73. package/dist/soundart-sketches/prismFlowFields.d.ts +0 -3
  74. package/dist/soundart-sketches/prismFlowFields.d.ts.map +0 -1
  75. package/dist/soundart-sketches/prismFlowFields.js +0 -51
  76. package/dist/soundart-sketches/radialBurst.d.ts +0 -3
  77. package/dist/soundart-sketches/radialBurst.d.ts.map +0 -1
  78. package/dist/soundart-sketches/radialBurst.js +0 -60
  79. package/dist/soundart-sketches/resonantSoundBodies.d.ts +0 -3
  80. package/dist/soundart-sketches/resonantSoundBodies.d.ts.map +0 -1
  81. package/dist/soundart-sketches/resonantSoundBodies.js +0 -89
  82. package/dist/soundart-sketches/rings.d.ts +0 -11
  83. package/dist/soundart-sketches/rings.d.ts.map +0 -1
  84. package/dist/soundart-sketches/rings.js +0 -89
  85. package/dist/soundart-sketches/squares.d.ts +0 -3
  86. package/dist/soundart-sketches/squares.d.ts.map +0 -1
  87. package/dist/soundart-sketches/squares.js +0 -52
  88. package/dist/soundart-sketches/waveStripes.d.ts +0 -3
  89. package/dist/soundart-sketches/waveStripes.d.ts.map +0 -1
  90. package/dist/soundart-sketches/waveStripes.js +0 -44
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK
3
- * Version: 1.1.0 (Protocol v1.0.0)
3
+ * Version: 1.4.0 (Protocol v1.2.0)
4
4
  *
5
5
  * ╔══════════════════════════════════════════════════════════════════════════╗
6
6
  * ║ @nexart/codemode-sdk — Canonical Code Mode Authority ║
@@ -10,46 +10,31 @@
10
10
  * ║ ║
11
11
  * ║ Protocol: nexart ║
12
12
  * ║ Engine: codemode ║
13
- * ║ SDK Version: 1.1.0 ║
14
- * ║ Protocol Version: 1.0.0 ║
15
- * ║ Phase: 1
13
+ * ║ SDK Version: 1.4.0 ║
14
+ * ║ Protocol Version: 1.2.0 ║
15
+ * ║ Phase: 3
16
16
  * ║ Enforcement: HARD ║
17
17
  * ╚══════════════════════════════════════════════════════════════════════════╝
18
18
  *
19
19
  * @example
20
20
  * ```typescript
21
- * import { executeCodeMode } from '@nexart/codemode-sdk';
21
+ * import { createEngine } from '@nexart/codemode-sdk';
22
22
  *
23
- * const result = await executeCodeMode({
24
- * source: `function setup() { background(255); ellipse(width/2, height/2, 100); }`,
25
- * width: 1950,
26
- * height: 2400,
27
- * seed: 12345,
28
- * vars: [50, 0, 0, 0, 0, 0, 0, 0, 0, 0],
29
- * mode: 'static'
30
- * });
23
+ * const engine = createEngine({ mode: 'static' });
31
24
  *
32
- * console.log(result.metadata.protocolVersion); // '1.0.0'
25
+ * engine.run({
26
+ * code: `
27
+ * function setup() {
28
+ * background(255);
29
+ * fill(0);
30
+ * ellipse(width/2, height/2, 100);
31
+ * }
32
+ * `,
33
+ * onComplete: (result) => {
34
+ * console.log('Rendered:', result.type, result.blob.size, 'bytes');
35
+ * }
36
+ * });
33
37
  * ```
34
38
  */
35
- // ═══════════════════════════════════════════════════════════════════════════
36
- // CANONICAL EXECUTION ENTRY POINT
37
- // ═══════════════════════════════════════════════════════════════════════════
38
- export { executeCodeMode, validateCodeModeSource } from './execute';
39
- export { PROTOCOL_IDENTITY } from './types';
40
- // ═══════════════════════════════════════════════════════════════════════════
41
- // LEGACY ENGINE API (use executeCodeMode for new implementations)
42
- // ═══════════════════════════════════════════════════════════════════════════
43
39
  export { createEngine } from './engine';
44
- export { DEFAULT_CONFIG } from './types';
45
- // SoundArt → Code Mode integration
46
- export { renderSoundArtViaCodeMode, canRenderViaCodeMode, getCodeModeAvailableStyles, } from './soundart-engine';
47
- export { createSoundSnapshot, createEmptySoundSnapshot, freezeSoundSnapshot, } from '../../shared/soundSnapshot';
48
- export { injectSoundGlobals, createSoundGlobals, createEmptySoundGlobals, generateSoundPalette, inferGenreProfile, createSoundHelpers, } from './sound-bridge';
49
- export { getSoundArtSketch, getAvailableSoundArtSketches, isSoundArtSketchAvailable, } from './soundart-sketches';
50
- export { createP5Runtime } from './p5-runtime';
51
- // Noise → Code Mode integration
52
- export { renderNoiseViaCodeMode, compileNoiseSystem, canRenderNoiseViaCodeMode, } from './noise-engine';
53
- export { createNoiseSnapshot, validateNoiseSnapshot, } from '../../shared/noiseSnapshot';
54
- export { createNoiseGlobals, injectNoiseGlobals, } from './noise-bridge';
55
- export { getNoiseSketch, getAvailableNoiseSketchNames, isValidNoiseSketch, } from './noise-sketches';
40
+ export { DEFAULT_CONFIG, PROTOCOL_IDENTITY } from './types';
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Loop Engine
3
- * Protocol: v1.0.0 (Phase 1) — HARD ENFORCEMENT
3
+ * Version: 1.4.0 (Protocol v1.2.0)
4
4
  *
5
5
  * Loop mode renderer: frame-authoritative, stateless execution.
6
6
  * - Executes setup() once
@@ -8,9 +8,6 @@
8
8
  * - Clears canvas before each frame (transparent)
9
9
  * - Injects normalized time variables
10
10
  * - No canvas persistence between frames
11
- *
12
- * Determinism Guarantee:
13
- * Same code + same seed + same VARs = identical frame sequence
14
11
  */
15
12
  import type { EngineConfig, RunOptions } from './types';
16
13
  export declare function cancelLoopMode(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../loop-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAA+B,MAAM,SAAS,CAAC;AAMrF,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Lf"}
1
+ {"version":3,"file":"loop-engine.d.ts","sourceRoot":"","sources":["../loop-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAA+B,MAAM,SAAS,CAAC;AAMrF,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Lf"}
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - Loop Engine
3
- * Protocol: v1.0.0 (Phase 1) — HARD ENFORCEMENT
3
+ * Version: 1.4.0 (Protocol v1.2.0)
4
4
  *
5
5
  * Loop mode renderer: frame-authoritative, stateless execution.
6
6
  * - Executes setup() once
@@ -8,18 +8,15 @@
8
8
  * - Clears canvas before each frame (transparent)
9
9
  * - Injects normalized time variables
10
10
  * - No canvas persistence between frames
11
- *
12
- * Determinism Guarantee:
13
- * Same code + same seed + same VARs = identical frame sequence
14
11
  */
15
12
  import { DEFAULT_CONFIG } from './types';
16
- import { createP5Runtime, injectProtocolVariables } from './p5-runtime';
13
+ import { createP5Runtime } from './p5-runtime';
17
14
  let isCancelled = false;
18
15
  export function cancelLoopMode() {
19
16
  isCancelled = true;
20
17
  }
21
18
  export async function runLoopMode(config, options) {
22
- const { code, seed, vars, onPreview, onProgress, onComplete, onError } = options;
19
+ const { code, onPreview, onProgress, onComplete, onError } = options;
23
20
  const width = config.width ?? DEFAULT_CONFIG.width;
24
21
  const height = config.height ?? DEFAULT_CONFIG.height;
25
22
  const duration = Math.max(DEFAULT_CONFIG.minDuration, Math.min(DEFAULT_CONFIG.maxDuration, config.duration ?? DEFAULT_CONFIG.duration));
@@ -36,10 +33,8 @@ export async function runLoopMode(config, options) {
36
33
  const canvas = document.createElement('canvas');
37
34
  canvas.width = width;
38
35
  canvas.height = height;
39
- // Create p5 runtime with optional seed for determinism
40
- const p = createP5Runtime(canvas, width, height, { seed });
41
- // Inject protocol variables (VAR[0..9])
42
- injectProtocolVariables(p, vars);
36
+ // Create p5 runtime
37
+ const p = createP5Runtime(canvas, width, height);
43
38
  // Validate code
44
39
  const hasDrawFunction = /function\s+draw\s*\(\s*\)/.test(code);
45
40
  if (!hasDrawFunction) {
@@ -69,16 +64,16 @@ export async function runLoopMode(config, options) {
69
64
  if (!drawCode) {
70
65
  throw new Error('Loop Mode requires a draw() function with content.');
71
66
  }
72
- // Create wrapped functions with p5 context, time variables, and VAR
73
- const wrappedSetup = new Function('p', 'frameCount', 't', 'time', 'tGlobal', 'VAR', `with(p) { ${setupCode} }`);
74
- const wrappedDraw = new Function('p', 'frameCount', 't', 'time', 'tGlobal', 'VAR', `with(p) { ${drawCode} }`);
67
+ // Create wrapped functions with p5 context and time variables
68
+ const wrappedSetup = new Function('p', 'frameCount', 't', 'time', 'tGlobal', `with(p) { ${setupCode} }`);
69
+ const wrappedDraw = new Function('p', 'frameCount', 't', 'time', 'tGlobal', `with(p) { ${drawCode} }`);
75
70
  onProgress?.({
76
71
  phase: 'setup',
77
72
  percent: 10,
78
73
  message: 'Executing setup()...',
79
74
  });
80
- // Execute setup() once with time = 0 and VAR
81
- wrappedSetup(p, 0, 0, 0, 0, p.VAR);
75
+ // Execute setup() once with time = 0
76
+ wrappedSetup(p, 0, 0, 0, 0);
82
77
  // Capture frames
83
78
  const frames = [];
84
79
  onProgress?.({
@@ -102,8 +97,8 @@ export async function runLoopMode(config, options) {
102
97
  // This enforces stateless, frame-authoritative rendering
103
98
  // Preserves artist-defined backgrounds (if they call background() in draw)
104
99
  p.clear();
105
- // Execute draw() with time variables and VAR
106
- wrappedDraw(p, frame, t, time, t, p.VAR);
100
+ // Execute draw() with time variables
101
+ wrappedDraw(p, frame, t, time, t);
107
102
  // Capture frame as PNG blob
108
103
  const blob = await new Promise((resolve, reject) => {
109
104
  canvas.toBlob((b) => b ? resolve(b) : reject(new Error(`Failed to capture frame ${frame}`)), 'image/png');
@@ -1,41 +1,11 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - p5-like Runtime
3
+ * Version: 1.4.0 (Protocol v1.2.0)
3
4
  *
4
- * ╔══════════════════════════════════════════════════════════════════════════╗
5
- * ║ CODE MODE PROTOCOL v1.0.0 (Phase 1) LOCKED ║
6
- * ║ ║
7
- * ║ Status: HARD PROTOCOL ENFORCEMENT ║
8
- * ║ This is the stable, canonical execution surface. ║
9
- * ║ SDKs, ByX, and external builders can depend on this API. ║
10
- * ║ ║
11
- * ║ Phase 1 Surface: ║
12
- * ║ - VAR[0..9]: 10 read-only protocol variables (0-100 range) ║
13
- * ║ - Drawing: line, rect, ellipse, circle, triangle, quad, arc, etc. ║
14
- * ║ - Style: fill, stroke, colorMode, strokeWeight ║
15
- * ║ - Transform: push, pop, translate, rotate, scale ║
16
- * ║ - Random: random(), randomSeed(), randomGaussian() (seeded) ║
17
- * ║ - Noise: noise(), noiseSeed(), noiseDetail() (seeded) ║
18
- * ║ - Math: map, constrain, lerp, lerpColor, dist, mag, norm ║
19
- * ║ - Color: Full CSS format support, color extraction functions ║
20
- * ║ - Time: frameCount, t, time, tGlobal ║
21
- * ║ ║
22
- * ║ Determinism Guarantees: ║
23
- * ║ - Same code + same seed + same VARs = identical output ║
24
- * ║ - No external state, no browser entropy, no time-based drift ║
25
- * ║ - Randomness ONLY from: random(), noise() (both seeded) ║
26
- * ║ ║
27
- * ║ ⚠️ Future changes require Phase 2+ ║
28
- * ╚══════════════════════════════════════════════════════════════════════════╝
5
+ * Minimal p5.js-like runtime for deterministic generative art execution.
6
+ * This is a headless runtime - no UI dependencies.
29
7
  */
30
8
  import type { TimeVariables } from './types';
31
- /**
32
- * Code Mode Protocol Version
33
- * This constant defines the locked protocol version.
34
- * Changes to the execution surface require a version bump.
35
- */
36
- export declare const CODE_MODE_PROTOCOL_VERSION = "1.0.0";
37
- export declare const CODE_MODE_PROTOCOL_PHASE = 1;
38
- export declare const CODE_MODE_ENFORCEMENT: "HARD";
39
9
  export interface P5Runtime {
40
10
  [key: string]: any;
41
11
  width: number;
@@ -46,28 +16,6 @@ export interface P5Runtime {
46
16
  HALF_PI: number;
47
17
  QUARTER_PI: number;
48
18
  }
49
- export interface P5RuntimeConfig {
50
- seed?: number;
51
- }
52
- export declare function createP5Runtime(canvas: HTMLCanvasElement, width: number, height: number, config?: P5RuntimeConfig): P5Runtime;
19
+ export declare function createP5Runtime(canvas: HTMLCanvasElement, width: number, height: number): P5Runtime;
53
20
  export declare function injectTimeVariables(p: P5Runtime, time: TimeVariables): void;
54
- /**
55
- * VAR Protocol Constants (Phase 1 — Protocol v1.0.0)
56
- * SDK v1.0.2: VAR input is optional (0-10 elements), but runtime always has 10
57
- */
58
- export declare const VAR_COUNT = 10;
59
- export declare const VAR_MIN = 0;
60
- export declare const VAR_MAX = 100;
61
- /**
62
- * Create a protected, read-only VAR array for protocol execution.
63
- *
64
- * SDK v1.0.2 Rules (Protocol v1.0.0):
65
- * - Input accepts 0-10 elements
66
- * - Runtime VAR is ALWAYS 10 elements (padded with zeros)
67
- * - Values are numeric, must be in 0-100 range (validated upstream)
68
- * - Read-only: writes throw descriptive errors
69
- * - Available in both setup() and draw()
70
- */
71
- export declare function createProtocolVAR(vars?: number[]): readonly number[];
72
- export declare function injectProtocolVariables(p: P5Runtime, vars?: number[]): void;
73
21
  //# sourceMappingURL=p5-runtime.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"p5-runtime.d.ts","sourceRoot":"","sources":["../p5-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,UAAU,CAAC;AAClD,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAC1C,eAAO,MAAM,qBAAqB,EAAG,MAAe,CAAC;AAErD,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAmED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,eAAe,GACvB,SAAS,CA0hBX;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAE3E;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,OAAO,MAAM,CAAC;AAE3B;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAmCpE;AAED,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAE3E"}
1
+ {"version":3,"file":"p5-runtime.d.ts","sourceRoot":"","sources":["../p5-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAE7C,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,iBAAiB,EACzB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACb,SAAS,CAqVX;AAED,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAE3E"}
@@ -1,97 +1,11 @@
1
1
  /**
2
2
  * NexArt Code Mode Runtime SDK - p5-like Runtime
3
+ * Version: 1.4.0 (Protocol v1.2.0)
3
4
  *
4
- * ╔══════════════════════════════════════════════════════════════════════════╗
5
- * ║ CODE MODE PROTOCOL v1.0.0 (Phase 1) LOCKED ║
6
- * ║ ║
7
- * ║ Status: HARD PROTOCOL ENFORCEMENT ║
8
- * ║ This is the stable, canonical execution surface. ║
9
- * ║ SDKs, ByX, and external builders can depend on this API. ║
10
- * ║ ║
11
- * ║ Phase 1 Surface: ║
12
- * ║ - VAR[0..9]: 10 read-only protocol variables (0-100 range) ║
13
- * ║ - Drawing: line, rect, ellipse, circle, triangle, quad, arc, etc. ║
14
- * ║ - Style: fill, stroke, colorMode, strokeWeight ║
15
- * ║ - Transform: push, pop, translate, rotate, scale ║
16
- * ║ - Random: random(), randomSeed(), randomGaussian() (seeded) ║
17
- * ║ - Noise: noise(), noiseSeed(), noiseDetail() (seeded) ║
18
- * ║ - Math: map, constrain, lerp, lerpColor, dist, mag, norm ║
19
- * ║ - Color: Full CSS format support, color extraction functions ║
20
- * ║ - Time: frameCount, t, time, tGlobal ║
21
- * ║ ║
22
- * ║ Determinism Guarantees: ║
23
- * ║ - Same code + same seed + same VARs = identical output ║
24
- * ║ - No external state, no browser entropy, no time-based drift ║
25
- * ║ - Randomness ONLY from: random(), noise() (both seeded) ║
26
- * ║ ║
27
- * ║ ⚠️ Future changes require Phase 2+ ║
28
- * ╚══════════════════════════════════════════════════════════════════════════╝
5
+ * Minimal p5.js-like runtime for deterministic generative art execution.
6
+ * This is a headless runtime - no UI dependencies.
29
7
  */
30
- /**
31
- * Code Mode Protocol Version
32
- * This constant defines the locked protocol version.
33
- * Changes to the execution surface require a version bump.
34
- */
35
- export const CODE_MODE_PROTOCOL_VERSION = '1.0.0';
36
- export const CODE_MODE_PROTOCOL_PHASE = 1;
37
- export const CODE_MODE_ENFORCEMENT = 'HARD';
38
- /**
39
- * Create a seeded random number generator (Mulberry32)
40
- * Same algorithm used in SoundArt for consistency
41
- */
42
- function createSeededRNG(seed = 123456) {
43
- let a = seed >>> 0;
44
- return () => {
45
- a += 0x6D2B79F5;
46
- let t = Math.imul(a ^ (a >>> 15), a | 1);
47
- t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
48
- return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
49
- };
50
- }
51
- /**
52
- * Create improved Perlin-like noise with seeding support
53
- */
54
- function createSeededNoise(seed = 0) {
55
- const permutation = [];
56
- const rng = createSeededRNG(seed);
57
- for (let i = 0; i < 256; i++) {
58
- permutation[i] = i;
59
- }
60
- for (let i = 255; i > 0; i--) {
61
- const j = Math.floor(rng() * (i + 1));
62
- [permutation[i], permutation[j]] = [permutation[j], permutation[i]];
63
- }
64
- for (let i = 0; i < 256; i++) {
65
- permutation[256 + i] = permutation[i];
66
- }
67
- const fade = (t) => t * t * t * (t * (t * 6 - 15) + 10);
68
- const lerp = (a, b, t) => a + t * (b - a);
69
- const grad = (hash, x, y, z) => {
70
- const h = hash & 15;
71
- const u = h < 8 ? x : y;
72
- const v = h < 4 ? y : h === 12 || h === 14 ? x : z;
73
- return ((h & 1) === 0 ? u : -u) + ((h & 2) === 0 ? v : -v);
74
- };
75
- return (x, y = 0, z = 0) => {
76
- const X = Math.floor(x) & 255;
77
- const Y = Math.floor(y) & 255;
78
- const Z = Math.floor(z) & 255;
79
- x -= Math.floor(x);
80
- y -= Math.floor(y);
81
- z -= Math.floor(z);
82
- const u = fade(x);
83
- const v = fade(y);
84
- const w = fade(z);
85
- const A = permutation[X] + Y;
86
- const AA = permutation[A] + Z;
87
- const AB = permutation[A + 1] + Z;
88
- const B = permutation[X + 1] + Y;
89
- const BA = permutation[B] + Z;
90
- const BB = permutation[B + 1] + Z;
91
- return (lerp(lerp(lerp(grad(permutation[AA], x, y, z), grad(permutation[BA], x - 1, y, z), u), lerp(grad(permutation[AB], x, y - 1, z), grad(permutation[BB], x - 1, y - 1, z), u), v), lerp(lerp(grad(permutation[AA + 1], x, y, z - 1), grad(permutation[BA + 1], x - 1, y, z - 1), u), lerp(grad(permutation[AB + 1], x, y - 1, z - 1), grad(permutation[BB + 1], x - 1, y - 1, z - 1), u), v), w) + 1) / 2;
92
- };
93
- }
94
- export function createP5Runtime(canvas, width, height, config) {
8
+ export function createP5Runtime(canvas, width, height) {
95
9
  const ctx = canvas.getContext('2d', { willReadFrequently: true });
96
10
  let currentFill = 'rgba(255, 255, 255, 1)';
97
11
  let currentStroke = 'rgba(0, 0, 0, 1)';
@@ -100,119 +14,14 @@ export function createP5Runtime(canvas, width, height, config) {
100
14
  let currentStrokeWeight = 1;
101
15
  let colorModeSettings = { mode: 'RGB', maxR: 255, maxG: 255, maxB: 255, maxA: 255 };
102
16
  let shapeStarted = false;
103
- // Seeded random state
104
- let randomSeedValue = config?.seed ?? Math.floor(Math.random() * 2147483647);
105
- let rng = createSeededRNG(randomSeedValue);
106
- // Seeded noise state
107
- let noiseSeedValue = config?.seed ?? 0;
108
- let noiseFunc = createSeededNoise(noiseSeedValue);
109
- let noiseOctaves = 4;
110
- let noiseFalloff = 0.5;
111
- /**
112
- * Parse CSS color string to normalized RGBA values
113
- * Supports: hex (#RGB, #RRGGBB, #RRGGBBAA), rgb(), rgba(), hsl(), hsla()
114
- */
115
- const parseCssColor = (str) => {
116
- const s = str.trim();
117
- // Hex format: #RGB, #RRGGBB, #RRGGBBAA
118
- if (s.startsWith('#')) {
119
- const hex = s.slice(1);
120
- if (hex.length === 3) {
121
- const r = parseInt(hex[0] + hex[0], 16);
122
- const g = parseInt(hex[1] + hex[1], 16);
123
- const b = parseInt(hex[2] + hex[2], 16);
124
- return { r, g, b, a: 1 };
125
- }
126
- else if (hex.length === 6) {
127
- const r = parseInt(hex.slice(0, 2), 16);
128
- const g = parseInt(hex.slice(2, 4), 16);
129
- const b = parseInt(hex.slice(4, 6), 16);
130
- return { r, g, b, a: 1 };
131
- }
132
- else if (hex.length === 8) {
133
- const r = parseInt(hex.slice(0, 2), 16);
134
- const g = parseInt(hex.slice(2, 4), 16);
135
- const b = parseInt(hex.slice(4, 6), 16);
136
- const a = parseInt(hex.slice(6, 8), 16) / 255;
137
- return { r, g, b, a };
138
- }
139
- }
140
- // rgb(r, g, b) or rgb(r g b)
141
- const rgbMatch = s.match(/^rgb\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*\)$/i);
142
- if (rgbMatch) {
143
- return {
144
- r: parseInt(rgbMatch[1]),
145
- g: parseInt(rgbMatch[2]),
146
- b: parseInt(rgbMatch[3]),
147
- a: 1
148
- };
149
- }
150
- // rgba(r, g, b, a) or rgba(r g b / a)
151
- const rgbaMatch = s.match(/^rgba\s*\(\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\s]\s*(\d+)\s*[,\/\s]\s*([\d.]+)\s*\)$/i);
152
- if (rgbaMatch) {
153
- return {
154
- r: parseInt(rgbaMatch[1]),
155
- g: parseInt(rgbaMatch[2]),
156
- b: parseInt(rgbaMatch[3]),
157
- a: parseFloat(rgbaMatch[4])
158
- };
159
- }
160
- // hsl(h, s%, l%) or hsla(h, s%, l%, a)
161
- const hslMatch = s.match(/^hsla?\s*\(\s*([\d.]+)\s*[,\s]\s*([\d.]+)%?\s*[,\s]\s*([\d.]+)%?\s*(?:[,\/\s]\s*([\d.]+))?\s*\)$/i);
162
- if (hslMatch) {
163
- const h = parseFloat(hslMatch[1]) / 360;
164
- const sat = parseFloat(hslMatch[2]) / 100;
165
- const l = parseFloat(hslMatch[3]) / 100;
166
- const a = hslMatch[4] ? parseFloat(hslMatch[4]) : 1;
167
- // HSL to RGB conversion
168
- let r, g, b;
169
- if (sat === 0) {
170
- r = g = b = l;
171
- }
172
- else {
173
- const hue2rgb = (p, q, t) => {
174
- if (t < 0)
175
- t += 1;
176
- if (t > 1)
177
- t -= 1;
178
- if (t < 1 / 6)
179
- return p + (q - p) * 6 * t;
180
- if (t < 1 / 2)
181
- return q;
182
- if (t < 2 / 3)
183
- return p + (q - p) * (2 / 3 - t) * 6;
184
- return p;
185
- };
186
- const q = l < 0.5 ? l * (1 + sat) : l + sat - l * sat;
187
- const p = 2 * l - q;
188
- r = hue2rgb(p, q, h + 1 / 3);
189
- g = hue2rgb(p, q, h);
190
- b = hue2rgb(p, q, h - 1 / 3);
191
- }
192
- return {
193
- r: Math.round(r * 255),
194
- g: Math.round(g * 255),
195
- b: Math.round(b * 255),
196
- a
197
- };
198
- }
199
- return null;
200
- };
201
17
  const parseColor = (...args) => {
202
18
  if (args.length === 0)
203
19
  return 'rgba(0, 0, 0, 1)';
204
20
  const { mode, maxR, maxG, maxB, maxA } = colorModeSettings;
205
21
  if (args.length === 1) {
206
22
  const val = args[0];
207
- if (typeof val === 'string') {
208
- // Try to parse CSS color formats
209
- const parsed = parseCssColor(val);
210
- if (parsed) {
211
- return `rgba(${parsed.r}, ${parsed.g}, ${parsed.b}, ${parsed.a})`;
212
- }
213
- // Return as-is for named colors (canvas handles them)
23
+ if (typeof val === 'string')
214
24
  return val;
215
- }
216
25
  if (mode === 'HSB') {
217
26
  return `hsla(${val}, 100%, 50%, 1)`;
218
27
  }
@@ -303,71 +112,7 @@ export function createP5Runtime(canvas, width, height, config) {
303
112
  },
304
113
  color: (...args) => parseColor(...args),
305
114
  lerpColor: (c1, c2, amt) => {
306
- // Parse both colors
307
- const color1 = parseCssColor(c1) || { r: 0, g: 0, b: 0, a: 1 };
308
- const color2 = parseCssColor(c2) || { r: 255, g: 255, b: 255, a: 1 };
309
- // Linearly interpolate each channel
310
- const r = Math.round(color1.r + (color2.r - color1.r) * amt);
311
- const g = Math.round(color1.g + (color2.g - color1.g) * amt);
312
- const b = Math.round(color1.b + (color2.b - color1.b) * amt);
313
- const a = color1.a + (color2.a - color1.a) * amt;
314
- return `rgba(${r}, ${g}, ${b}, ${a})`;
315
- },
316
- red: (color) => {
317
- const parsed = parseCssColor(color);
318
- return parsed ? parsed.r : 0;
319
- },
320
- green: (color) => {
321
- const parsed = parseCssColor(color);
322
- return parsed ? parsed.g : 0;
323
- },
324
- blue: (color) => {
325
- const parsed = parseCssColor(color);
326
- return parsed ? parsed.b : 0;
327
- },
328
- alpha: (color) => {
329
- const parsed = parseCssColor(color);
330
- return parsed ? parsed.a * 255 : 255;
331
- },
332
- brightness: (color) => {
333
- const parsed = parseCssColor(color);
334
- if (!parsed)
335
- return 0;
336
- return Math.max(parsed.r, parsed.g, parsed.b) / 255 * 100;
337
- },
338
- saturation: (color) => {
339
- const parsed = parseCssColor(color);
340
- if (!parsed)
341
- return 0;
342
- const max = Math.max(parsed.r, parsed.g, parsed.b);
343
- const min = Math.min(parsed.r, parsed.g, parsed.b);
344
- if (max === 0)
345
- return 0;
346
- return ((max - min) / max) * 100;
347
- },
348
- hue: (color) => {
349
- const parsed = parseCssColor(color);
350
- if (!parsed)
351
- return 0;
352
- const { r, g, b } = parsed;
353
- const max = Math.max(r, g, b);
354
- const min = Math.min(r, g, b);
355
- if (max === min)
356
- return 0;
357
- let h = 0;
358
- const d = max - min;
359
- switch (max) {
360
- case r:
361
- h = ((g - b) / d + (g < b ? 6 : 0)) / 6;
362
- break;
363
- case g:
364
- h = ((b - r) / d + 2) / 6;
365
- break;
366
- case b:
367
- h = ((r - g) / d + 4) / 6;
368
- break;
369
- }
370
- return h * 360;
115
+ return c1; // Simplified - full implementation would blend colors
371
116
  },
372
117
  // Shape functions
373
118
  ellipse: (x, y, w, h) => {
@@ -497,53 +242,30 @@ export function createP5Runtime(canvas, width, height, config) {
497
242
  resetMatrix: () => {
498
243
  ctx.setTransform(1, 0, 0, 1, 0, 0);
499
244
  },
500
- // Math functions - SEEDED for determinism
245
+ // Math functions
501
246
  random: (min, max) => {
502
- // Support random() with arrays
503
- if (Array.isArray(min)) {
504
- return min[Math.floor(rng() * min.length)];
505
- }
506
247
  if (min === undefined)
507
- return rng();
248
+ return Math.random();
508
249
  if (max === undefined)
509
- return rng() * min;
510
- return min + rng() * (max - min);
250
+ return Math.random() * min;
251
+ return min + Math.random() * (max - min);
511
252
  },
512
253
  randomSeed: (seed) => {
513
- randomSeedValue = seed;
514
- rng = createSeededRNG(seed);
515
- },
516
- randomGaussian: (mean = 0, sd = 1) => {
517
- // Box-Muller transform for Gaussian distribution
518
- const u1 = rng();
519
- const u2 = rng();
520
- const z0 = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
521
- return z0 * sd + mean;
254
+ // Simplified - would need proper seeded random
522
255
  },
523
256
  noise: (x, y, z) => {
524
- // Use seeded Perlin noise with octaves
525
- let total = 0;
526
- let frequency = 1;
527
- let amplitude = 1;
528
- let maxValue = 0;
529
- for (let i = 0; i < noiseOctaves; i++) {
530
- total += noiseFunc(x * frequency, (y ?? 0) * frequency, (z ?? 0) * frequency) * amplitude;
531
- maxValue += amplitude;
532
- amplitude *= noiseFalloff;
533
- frequency *= 2;
534
- }
535
- return total / maxValue;
536
- },
537
- noiseSeed: (seed) => {
538
- noiseSeedValue = seed;
539
- noiseFunc = createSeededNoise(seed);
540
- },
541
- noiseDetail: (lod, falloff) => {
542
- noiseOctaves = Math.max(1, Math.min(8, lod));
543
- if (falloff !== undefined) {
544
- noiseFalloff = Math.max(0, Math.min(1, falloff));
545
- }
257
+ // Simplified Perlin-like noise
258
+ const hash = (n) => {
259
+ const s = Math.sin(n) * 43758.5453123;
260
+ return s - Math.floor(s);
261
+ };
262
+ const xVal = x ?? 0;
263
+ const yVal = y ?? 0;
264
+ const zVal = z ?? 0;
265
+ return hash(xVal * 12.9898 + yVal * 78.233 + zVal * 37.719);
546
266
  },
267
+ noiseSeed: (seed) => { },
268
+ noiseDetail: (lod, falloff) => { },
547
269
  map: (value, start1, stop1, start2, stop2) => {
548
270
  return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1));
549
271
  },
@@ -594,51 +316,3 @@ export function createP5Runtime(canvas, width, height, config) {
594
316
  export function injectTimeVariables(p, time) {
595
317
  p.frameCount = time.frameCount;
596
318
  }
597
- /**
598
- * VAR Protocol Constants (Phase 1 — Protocol v1.0.0)
599
- * SDK v1.0.2: VAR input is optional (0-10 elements), but runtime always has 10
600
- */
601
- export const VAR_COUNT = 10; // Exactly 10 protocol variables: VAR[0..9]
602
- export const VAR_MIN = 0; // Minimum value
603
- export const VAR_MAX = 100; // Maximum value (normalized range)
604
- /**
605
- * Create a protected, read-only VAR array for protocol execution.
606
- *
607
- * SDK v1.0.2 Rules (Protocol v1.0.0):
608
- * - Input accepts 0-10 elements
609
- * - Runtime VAR is ALWAYS 10 elements (padded with zeros)
610
- * - Values are numeric, must be in 0-100 range (validated upstream)
611
- * - Read-only: writes throw descriptive errors
612
- * - Available in both setup() and draw()
613
- */
614
- export function createProtocolVAR(vars) {
615
- // Create frozen 10-element array (upstream normalizeVars ensures this)
616
- const normalizedVars = [];
617
- for (let i = 0; i < VAR_COUNT; i++) {
618
- normalizedVars[i] = vars?.[i] ?? 0;
619
- }
620
- // Freeze the array to prevent modifications
621
- const frozenVars = Object.freeze(normalizedVars);
622
- // Wrap in Proxy for descriptive error messages on write attempts
623
- return new Proxy(frozenVars, {
624
- set(_target, prop, _value) {
625
- const propName = typeof prop === 'symbol' ? prop.toString() : prop;
626
- throw new Error(`[Code Mode Protocol Error] VAR is read-only. ` +
627
- `Cannot write to VAR[${propName}]. ` +
628
- `VAR[0..9] are protocol inputs, not sketch state.`);
629
- },
630
- deleteProperty(_target, prop) {
631
- const propName = typeof prop === 'symbol' ? prop.toString() : prop;
632
- throw new Error(`[Code Mode Protocol Error] VAR is read-only. ` +
633
- `Cannot delete VAR[${propName}].`);
634
- },
635
- defineProperty(_target, prop) {
636
- const propName = typeof prop === 'symbol' ? prop.toString() : prop;
637
- throw new Error(`[Code Mode Protocol Error] Cannot define new VAR properties. ` +
638
- `VAR is fixed at 10 elements (VAR[0..9]). Attempted: ${propName}`);
639
- },
640
- });
641
- }
642
- export function injectProtocolVariables(p, vars) {
643
- p.VAR = createProtocolVAR(vars);
644
- }