@nexart/codemode-sdk 1.6.0 → 1.8.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 (121) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/README.md +221 -9
  3. package/dist/core-index.d.ts +1 -1
  4. package/dist/core-index.js +1 -1
  5. package/dist/entry/browser.d.ts +43 -0
  6. package/dist/entry/browser.d.ts.map +1 -0
  7. package/dist/entry/browser.js +63 -0
  8. package/dist/entry/node.d.ts +23 -0
  9. package/dist/entry/node.d.ts.map +1 -0
  10. package/dist/entry/node.js +34 -0
  11. package/dist/index.d.ts +14 -33
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +41 -33
  14. package/dist/runtime.d.ts +52 -0
  15. package/dist/runtime.d.ts.map +1 -0
  16. package/dist/runtime.js +219 -0
  17. package/dist/sdk/codemode/builder-manifest.d.ts +79 -0
  18. package/dist/sdk/codemode/builder-manifest.d.ts.map +1 -0
  19. package/dist/sdk/codemode/builder-manifest.js +97 -0
  20. package/dist/sdk/codemode/core-index.d.ts +21 -0
  21. package/dist/sdk/codemode/core-index.d.ts.map +1 -0
  22. package/dist/sdk/codemode/core-index.js +26 -0
  23. package/dist/sdk/codemode/engine.d.ts +24 -0
  24. package/dist/sdk/codemode/engine.d.ts.map +1 -0
  25. package/dist/sdk/codemode/engine.js +67 -0
  26. package/dist/sdk/codemode/execute.d.ts +46 -0
  27. package/dist/sdk/codemode/execute.d.ts.map +1 -0
  28. package/dist/sdk/codemode/execute.js +283 -0
  29. package/dist/sdk/codemode/execution-sandbox.d.ts +107 -0
  30. package/dist/sdk/codemode/execution-sandbox.d.ts.map +1 -0
  31. package/dist/sdk/codemode/execution-sandbox.js +207 -0
  32. package/dist/sdk/codemode/index.d.ts +31 -0
  33. package/dist/sdk/codemode/index.d.ts.map +1 -0
  34. package/dist/sdk/codemode/index.js +63 -0
  35. package/dist/sdk/codemode/loop-engine.d.ts +22 -0
  36. package/dist/sdk/codemode/loop-engine.d.ts.map +1 -0
  37. package/dist/sdk/codemode/loop-engine.js +229 -0
  38. package/dist/sdk/codemode/noise-bridge.d.ts +44 -0
  39. package/dist/sdk/codemode/noise-bridge.d.ts.map +1 -0
  40. package/dist/sdk/codemode/noise-bridge.js +68 -0
  41. package/dist/sdk/codemode/noise-engine.d.ts +74 -0
  42. package/dist/sdk/codemode/noise-engine.d.ts.map +1 -0
  43. package/dist/sdk/codemode/noise-engine.js +132 -0
  44. package/dist/sdk/codemode/noise-sketches/fractalNoise.d.ts +11 -0
  45. package/dist/sdk/codemode/noise-sketches/fractalNoise.d.ts.map +1 -0
  46. package/dist/sdk/codemode/noise-sketches/fractalNoise.js +121 -0
  47. package/dist/sdk/codemode/noise-sketches/index.d.ts +21 -0
  48. package/dist/sdk/codemode/noise-sketches/index.d.ts.map +1 -0
  49. package/dist/sdk/codemode/noise-sketches/index.js +28 -0
  50. package/dist/sdk/codemode/p5-runtime.d.ts +75 -0
  51. package/dist/sdk/codemode/p5-runtime.d.ts.map +1 -0
  52. package/dist/sdk/codemode/p5-runtime.js +1031 -0
  53. package/dist/sdk/codemode/sound-bridge.d.ts +89 -0
  54. package/dist/sdk/codemode/sound-bridge.d.ts.map +1 -0
  55. package/dist/sdk/codemode/sound-bridge.js +128 -0
  56. package/dist/sdk/codemode/soundart-engine.d.ts +87 -0
  57. package/dist/sdk/codemode/soundart-engine.d.ts.map +1 -0
  58. package/dist/sdk/codemode/soundart-engine.js +173 -0
  59. package/dist/sdk/codemode/soundart-sketches/chladniBloom.d.ts +3 -0
  60. package/dist/sdk/codemode/soundart-sketches/chladniBloom.d.ts.map +1 -0
  61. package/dist/sdk/codemode/soundart-sketches/chladniBloom.js +53 -0
  62. package/dist/sdk/codemode/soundart-sketches/dualVortex.d.ts +3 -0
  63. package/dist/sdk/codemode/soundart-sketches/dualVortex.d.ts.map +1 -0
  64. package/dist/sdk/codemode/soundart-sketches/dualVortex.js +67 -0
  65. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.d.ts +3 -0
  66. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.d.ts.map +1 -0
  67. package/dist/sdk/codemode/soundart-sketches/geometryIllusion.js +89 -0
  68. package/dist/sdk/codemode/soundart-sketches/index.d.ts +39 -0
  69. package/dist/sdk/codemode/soundart-sketches/index.d.ts.map +1 -0
  70. package/dist/sdk/codemode/soundart-sketches/index.js +72 -0
  71. package/dist/sdk/codemode/soundart-sketches/isoflow.d.ts +3 -0
  72. package/dist/sdk/codemode/soundart-sketches/isoflow.d.ts.map +1 -0
  73. package/dist/sdk/codemode/soundart-sketches/isoflow.js +60 -0
  74. package/dist/sdk/codemode/soundart-sketches/loomWeave.d.ts +3 -0
  75. package/dist/sdk/codemode/soundart-sketches/loomWeave.d.ts.map +1 -0
  76. package/dist/sdk/codemode/soundart-sketches/loomWeave.js +59 -0
  77. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.d.ts +3 -0
  78. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.d.ts.map +1 -0
  79. package/dist/sdk/codemode/soundart-sketches/noiseTerraces.js +53 -0
  80. package/dist/sdk/codemode/soundart-sketches/orb.d.ts +3 -0
  81. package/dist/sdk/codemode/soundart-sketches/orb.d.ts.map +1 -0
  82. package/dist/sdk/codemode/soundart-sketches/orb.js +50 -0
  83. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts +3 -0
  84. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.d.ts.map +1 -0
  85. package/dist/sdk/codemode/soundart-sketches/pixelGlyphs.js +72 -0
  86. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.d.ts +3 -0
  87. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.d.ts.map +1 -0
  88. package/dist/sdk/codemode/soundart-sketches/prismFlowFields.js +51 -0
  89. package/dist/sdk/codemode/soundart-sketches/radialBurst.d.ts +3 -0
  90. package/dist/sdk/codemode/soundart-sketches/radialBurst.d.ts.map +1 -0
  91. package/dist/sdk/codemode/soundart-sketches/radialBurst.js +60 -0
  92. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts +3 -0
  93. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.d.ts.map +1 -0
  94. package/dist/sdk/codemode/soundart-sketches/resonantSoundBodies.js +89 -0
  95. package/dist/sdk/codemode/soundart-sketches/rings.d.ts +11 -0
  96. package/dist/sdk/codemode/soundart-sketches/rings.d.ts.map +1 -0
  97. package/dist/sdk/codemode/soundart-sketches/rings.js +89 -0
  98. package/dist/sdk/codemode/soundart-sketches/squares.d.ts +3 -0
  99. package/dist/sdk/codemode/soundart-sketches/squares.d.ts.map +1 -0
  100. package/dist/sdk/codemode/soundart-sketches/squares.js +52 -0
  101. package/dist/sdk/codemode/soundart-sketches/waveStripes.d.ts +3 -0
  102. package/dist/sdk/codemode/soundart-sketches/waveStripes.d.ts.map +1 -0
  103. package/dist/sdk/codemode/soundart-sketches/waveStripes.js +44 -0
  104. package/dist/sdk/codemode/static-engine.d.ts +20 -0
  105. package/dist/sdk/codemode/static-engine.d.ts.map +1 -0
  106. package/dist/sdk/codemode/static-engine.js +144 -0
  107. package/dist/sdk/codemode/types.d.ts +191 -0
  108. package/dist/sdk/codemode/types.d.ts.map +1 -0
  109. package/dist/sdk/codemode/types.js +32 -0
  110. package/dist/shared/noiseSnapshot.d.ts +59 -0
  111. package/dist/shared/noiseSnapshot.d.ts.map +1 -0
  112. package/dist/shared/noiseSnapshot.js +72 -0
  113. package/dist/shared/soundSnapshot.d.ts +94 -0
  114. package/dist/shared/soundSnapshot.d.ts.map +1 -0
  115. package/dist/shared/soundSnapshot.js +128 -0
  116. package/dist/static-engine.d.ts.map +1 -1
  117. package/dist/static-engine.js +24 -11
  118. package/examples/basic.ts +61 -0
  119. package/examples/preflight-test.ts +275 -0
  120. package/examples/verify.ts +151 -0
  121. package/package.json +45 -5
@@ -0,0 +1,191 @@
1
+ /**
2
+ * NexArt Code Mode Runtime SDK - Types
3
+ * Version: 1.6.0 (Protocol v1.2.0)
4
+ *
5
+ * Type definitions for the Code Mode runtime engine.
6
+ * This is the canonical type surface for @nexart/codemode-sdk.
7
+ */
8
+ /**
9
+ * Protocol Constants
10
+ * These define the locked protocol identity.
11
+ */
12
+ export declare const PROTOCOL_IDENTITY: {
13
+ readonly protocol: "nexart";
14
+ readonly engine: "codemode";
15
+ readonly protocolVersion: "1.2.0";
16
+ readonly phase: 3;
17
+ readonly deterministic: true;
18
+ };
19
+ export type RenderMode = 'static' | 'loop';
20
+ /**
21
+ * Runtime Canvas
22
+ * Abstraction for browser (HTMLCanvasElement) and Node (canvas package) environments.
23
+ * In Node, the `canvas` npm package provides an HTMLCanvasElement-compatible interface.
24
+ */
25
+ export type RuntimeCanvas = HTMLCanvasElement;
26
+ export interface EngineConfig {
27
+ mode: RenderMode;
28
+ width?: number;
29
+ height?: number;
30
+ duration?: number;
31
+ fps?: number;
32
+ }
33
+ /**
34
+ * Render Result (Discriminated Union)
35
+ *
36
+ * Static mode can return either:
37
+ * - blob: For display/download (PNG)
38
+ * - imageData: For oracle hashing (raw pixels)
39
+ *
40
+ * Video mode returns blob with frame/duration metadata.
41
+ */
42
+ export type RenderResult = {
43
+ type: 'image';
44
+ blob: Blob;
45
+ } | {
46
+ type: 'image';
47
+ imageData: ImageData;
48
+ } | {
49
+ type: 'video';
50
+ blob: Blob;
51
+ frames?: number;
52
+ duration?: number;
53
+ };
54
+ export interface RunOptions {
55
+ code: string;
56
+ seed?: number;
57
+ vars?: number[];
58
+ onPreview?: (canvas: RuntimeCanvas) => void;
59
+ onProgress?: (progress: ProgressInfo) => void;
60
+ onComplete: (result: RenderResult) => void;
61
+ onError?: (error: Error) => void;
62
+ returnImageData?: boolean;
63
+ }
64
+ export interface ProgressInfo {
65
+ phase: 'setup' | 'rendering' | 'encoding' | 'complete';
66
+ frame?: number;
67
+ totalFrames?: number;
68
+ percent: number;
69
+ message: string;
70
+ }
71
+ export interface Engine {
72
+ run: (options: RunOptions) => Promise<void>;
73
+ stop: () => void;
74
+ getConfig: () => Readonly<EngineConfig>;
75
+ }
76
+ export interface TimeVariables {
77
+ frameCount: number;
78
+ t: number;
79
+ time: number;
80
+ tGlobal: number;
81
+ totalFrames: number;
82
+ }
83
+ /**
84
+ * Protocol Variables (VAR[0..9])
85
+ * First-class protocol inputs for deterministic rendering.
86
+ * Used by SoundArt, Shapes, Noise, and ByX collections.
87
+ *
88
+ * Rules (SDK v1.0.2, Protocol v1.0.0):
89
+ * - Input array can have 0-10 elements (protocol error if > 10)
90
+ * - All values MUST be finite numbers (protocol error if not)
91
+ * - Values MUST be in range 0-100 (protocol error if out of range, NO clamping)
92
+ * - Runtime VAR is ALWAYS 10 elements (padded with zeros for consistency)
93
+ * - Read-only inside sketches (write attempts throw protocol error)
94
+ * - Default: all zeros if not provided
95
+ */
96
+ export interface ProtocolVariables {
97
+ VAR: readonly [number, number, number, number, number, number, number, number, number, number];
98
+ }
99
+ /**
100
+ * Default protocol variables (all zeros)
101
+ */
102
+ export declare const DEFAULT_VARS: ProtocolVariables;
103
+ export declare const DEFAULT_CONFIG: {
104
+ readonly width: 1950;
105
+ readonly height: 2400;
106
+ readonly duration: 2;
107
+ readonly fps: 30;
108
+ readonly minDuration: 1;
109
+ readonly maxDuration: 4;
110
+ };
111
+ /**
112
+ * Protocol Metadata
113
+ * Attached to every execution result for verification.
114
+ */
115
+ export interface ProtocolMetadata {
116
+ protocol: 'nexart';
117
+ engine: 'codemode';
118
+ protocolVersion: '1.2.0';
119
+ phase: 3;
120
+ deterministic: true;
121
+ seed: number;
122
+ vars: number[];
123
+ width: number;
124
+ height: number;
125
+ mode: RenderMode;
126
+ totalFrames?: number;
127
+ }
128
+ /**
129
+ * Canonical Execution Input
130
+ * The single entry point for all Code Mode execution.
131
+ */
132
+ export interface ExecuteCodeModeInput {
133
+ source: string;
134
+ width: number;
135
+ height: number;
136
+ seed: number;
137
+ vars?: number[];
138
+ mode: RenderMode;
139
+ totalFrames?: number;
140
+ }
141
+ /**
142
+ * Canonical Execution Result
143
+ * Every execution produces this structure.
144
+ */
145
+ export interface ExecuteCodeModeResult {
146
+ image?: Blob;
147
+ video?: Blob;
148
+ frames?: ImageData[];
149
+ metadata: ProtocolMetadata;
150
+ }
151
+ /**
152
+ * Builder Manifest (v1.6.0)
153
+ *
154
+ * Passive, declarative metadata for builder attribution.
155
+ * This is data-only with no logic, rewards, or enforcement attached.
156
+ *
157
+ * Rules:
158
+ * - Registration is optional
159
+ * - Missing or invalid manifest does NOT throw
160
+ * - No network calls, no telemetry, no analytics
161
+ * - Does NOT affect execution behavior or determinism
162
+ * - Used for future attribution and ecosystem discovery
163
+ */
164
+ export interface NexArtBuilderManifest {
165
+ protocol: 'nexart';
166
+ manifestVersion: string;
167
+ app?: {
168
+ name?: string;
169
+ url?: string;
170
+ description?: string;
171
+ contact?: string;
172
+ };
173
+ sdk?: {
174
+ package?: string;
175
+ version?: string;
176
+ execution?: string;
177
+ };
178
+ renderer?: {
179
+ package?: string;
180
+ version?: string;
181
+ mode?: 'preview' | 'canonical';
182
+ };
183
+ features?: Record<string, boolean>;
184
+ declaration?: {
185
+ usesOfficialSdk?: boolean;
186
+ noRuntimeModification?: boolean;
187
+ noProtocolBypass?: boolean;
188
+ };
189
+ timestamp?: string;
190
+ }
191
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * NexArt Code Mode Runtime SDK - Types
3
+ * Version: 1.6.0 (Protocol v1.2.0)
4
+ *
5
+ * Type definitions for the Code Mode runtime engine.
6
+ * This is the canonical type surface for @nexart/codemode-sdk.
7
+ */
8
+ /**
9
+ * Protocol Constants
10
+ * These define the locked protocol identity.
11
+ */
12
+ export const PROTOCOL_IDENTITY = {
13
+ protocol: 'nexart',
14
+ engine: 'codemode',
15
+ protocolVersion: '1.2.0',
16
+ phase: 3,
17
+ deterministic: true,
18
+ };
19
+ /**
20
+ * Default protocol variables (all zeros)
21
+ */
22
+ export const DEFAULT_VARS = {
23
+ VAR: Object.freeze([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
24
+ };
25
+ export const DEFAULT_CONFIG = {
26
+ width: 1950,
27
+ height: 2400,
28
+ duration: 2,
29
+ fps: 30,
30
+ minDuration: 1,
31
+ maxDuration: 4,
32
+ };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Noise Snapshot Types
3
+ *
4
+ * This module defines the canonical NoiseSnapshot interface used by the Code Mode runtime.
5
+ * All noise parameters are normalized to deterministic, reproducible values.
6
+ *
7
+ * Architecture:
8
+ * NoiseState (UI) → NoiseSnapshot (normalized) → N.* globals → Code Mode Runtime
9
+ */
10
+ /**
11
+ * Blend modes for combining Perlin and Cellular noise
12
+ */
13
+ export type NoiseBlendMode = 'perlin_only' | 'blend' | 'warp' | 'interleave';
14
+ /**
15
+ * Raw noise parameters from the UI
16
+ */
17
+ export interface NoiseParams {
18
+ blendMode: NoiseBlendMode;
19
+ scale: number;
20
+ octaves: number;
21
+ persistence: number;
22
+ lacunarity: number;
23
+ cellDensity: number;
24
+ cellDistortion: number;
25
+ seed: number;
26
+ backgroundColor: string;
27
+ noiseColor: string;
28
+ zoomLevel: number;
29
+ }
30
+ /**
31
+ * Normalized snapshot for Code Mode injection
32
+ * All values are designed for deterministic rendering
33
+ */
34
+ export interface NoiseSnapshot {
35
+ scale: number;
36
+ octaves: number;
37
+ persistence: number;
38
+ lacunarity: number;
39
+ cellDensity: number;
40
+ cellDistortion: number;
41
+ blendMode: NoiseBlendMode;
42
+ bgR: number;
43
+ bgG: number;
44
+ bgB: number;
45
+ noiseR: number;
46
+ noiseG: number;
47
+ noiseB: number;
48
+ seed: number;
49
+ zoomLevel: number;
50
+ }
51
+ /**
52
+ * Create a NoiseSnapshot from raw NoiseParams
53
+ */
54
+ export declare function createNoiseSnapshot(params: NoiseParams): NoiseSnapshot;
55
+ /**
56
+ * Validate and clamp noise snapshot values
57
+ */
58
+ export declare function validateNoiseSnapshot(snapshot: NoiseSnapshot): NoiseSnapshot;
59
+ //# sourceMappingURL=noiseSnapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"noiseSnapshot.d.ts","sourceRoot":"","sources":["../../../../shared/noiseSnapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;AAE7E;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,cAAc,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAE5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IAGnB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IAGvB,SAAS,EAAE,cAAc,CAAC;IAG1B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IAGf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAmBD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,CAqBtE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,aAAa,CAmB5E"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Noise Snapshot Types
3
+ *
4
+ * This module defines the canonical NoiseSnapshot interface used by the Code Mode runtime.
5
+ * All noise parameters are normalized to deterministic, reproducible values.
6
+ *
7
+ * Architecture:
8
+ * NoiseState (UI) → NoiseSnapshot (normalized) → N.* globals → Code Mode Runtime
9
+ */
10
+ /**
11
+ * Parse a hex color string to RGB values
12
+ */
13
+ function hexToRgb(hex) {
14
+ if (!hex || !hex.startsWith('#') || hex.length !== 7) {
15
+ return [255, 255, 255];
16
+ }
17
+ try {
18
+ const r = parseInt(hex.slice(1, 3), 16);
19
+ const g = parseInt(hex.slice(3, 5), 16);
20
+ const b = parseInt(hex.slice(5, 7), 16);
21
+ return [r, g, b];
22
+ }
23
+ catch {
24
+ return [255, 255, 255];
25
+ }
26
+ }
27
+ /**
28
+ * Create a NoiseSnapshot from raw NoiseParams
29
+ */
30
+ export function createNoiseSnapshot(params) {
31
+ const [bgR, bgG, bgB] = hexToRgb(params.backgroundColor);
32
+ const [noiseR, noiseG, noiseB] = hexToRgb(params.noiseColor);
33
+ return {
34
+ scale: params.scale,
35
+ octaves: Math.floor(params.octaves),
36
+ persistence: params.persistence,
37
+ lacunarity: params.lacunarity,
38
+ cellDensity: params.cellDensity,
39
+ cellDistortion: params.cellDistortion,
40
+ blendMode: params.blendMode,
41
+ bgR,
42
+ bgG,
43
+ bgB,
44
+ noiseR,
45
+ noiseG,
46
+ noiseB,
47
+ seed: params.seed,
48
+ zoomLevel: params.zoomLevel,
49
+ };
50
+ }
51
+ /**
52
+ * Validate and clamp noise snapshot values
53
+ */
54
+ export function validateNoiseSnapshot(snapshot) {
55
+ const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
56
+ return {
57
+ ...snapshot,
58
+ scale: clamp(snapshot.scale, 0.001, 0.1),
59
+ octaves: clamp(Math.floor(snapshot.octaves), 1, 8),
60
+ persistence: clamp(snapshot.persistence, 0.1, 1.0),
61
+ lacunarity: clamp(snapshot.lacunarity, 1.0, 3.0),
62
+ cellDensity: clamp(snapshot.cellDensity, 0.1, 1.0),
63
+ cellDistortion: clamp(snapshot.cellDistortion, 0.1, 2.0),
64
+ bgR: clamp(snapshot.bgR, 0, 255),
65
+ bgG: clamp(snapshot.bgG, 0, 255),
66
+ bgB: clamp(snapshot.bgB, 0, 255),
67
+ noiseR: clamp(snapshot.noiseR, 0, 255),
68
+ noiseG: clamp(snapshot.noiseG, 0, 255),
69
+ noiseB: clamp(snapshot.noiseB, 0, 255),
70
+ zoomLevel: clamp(snapshot.zoomLevel, 0.1, 2.0),
71
+ };
72
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * SoundSnapshot - Canonical sound data interface for Code Mode integration
3
+ *
4
+ * This is the single source of truth for sound-derived parameters.
5
+ * All values are normalized to 0-100 range for consistency.
6
+ *
7
+ * Usage:
8
+ * - Sound analysis produces SoundSnapshot
9
+ * - Code Mode receives it as read-only S.* globals
10
+ * - SoundArt styles become Code Mode sketches using S.*
11
+ */
12
+ export interface SoundSnapshot {
13
+ volume: number;
14
+ amplitude: number;
15
+ dynamicRange: number;
16
+ brightness: number;
17
+ bass: number;
18
+ mid: number;
19
+ treble: number;
20
+ harmonicity: number;
21
+ aggression: number;
22
+ attack: number;
23
+ rhythmicity: number;
24
+ silence: number;
25
+ hue: number;
26
+ chroma: number;
27
+ length: number;
28
+ t: number;
29
+ frame?: number;
30
+ totalFrames?: number;
31
+ }
32
+ /**
33
+ * Create an empty/default SoundSnapshot with neutral values
34
+ */
35
+ export declare function createEmptySoundSnapshot(): SoundSnapshot;
36
+ /**
37
+ * Clamp a value to 0-100 range
38
+ */
39
+ export declare function clampPercent(value: number): number;
40
+ /**
41
+ * Convert a 0-1 normalized value to 0-100 percentage
42
+ */
43
+ export declare function toPercent(value: number): number;
44
+ /**
45
+ * SoundFeatures type for compatibility with existing analysis code
46
+ */
47
+ export interface SoundBands {
48
+ sub?: number;
49
+ bass: number;
50
+ lowMid: number;
51
+ highMid: number;
52
+ treble: number;
53
+ }
54
+ export interface FrameFeatures {
55
+ t0: number;
56
+ t1: number;
57
+ rms: number;
58
+ centroid: number;
59
+ bands: SoundBands;
60
+ aggression: number;
61
+ amplitude: number;
62
+ dynamicRange: number;
63
+ attack: number;
64
+ rhythmicity: number;
65
+ harmonicity: number;
66
+ silence: number;
67
+ tempoBpm?: number;
68
+ }
69
+ export interface SoundFeatures {
70
+ rms: number;
71
+ centroid: number;
72
+ bands: SoundBands;
73
+ durationSec: number;
74
+ seedHint?: number;
75
+ frames: FrameFeatures[];
76
+ averageBpm?: number;
77
+ featureVariances?: Record<string, number>;
78
+ }
79
+ /**
80
+ * Create a SoundSnapshot from SoundFeatures (analysis output)
81
+ *
82
+ * @param sound - Raw sound features from analysis
83
+ * @param frames - Frame features array
84
+ * @param windowMs - Time window to average (default 800ms for recent snapshot)
85
+ * @param frameIndex - Current frame index for loop mode
86
+ * @param totalFrames - Total frames for loop mode
87
+ */
88
+ export declare function createSoundSnapshot(sound: SoundFeatures, frames?: FrameFeatures[], windowMs?: number, frameIndex?: number, totalFrames?: number): SoundSnapshot;
89
+ /**
90
+ * Freeze a SoundSnapshot to make it immutable
91
+ * This is used when injecting into Code Mode to prevent modification
92
+ */
93
+ export declare function freezeSoundSnapshot(snapshot: SoundSnapshot): Readonly<SoundSnapshot>;
94
+ //# sourceMappingURL=soundSnapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"soundSnapshot.d.ts","sourceRoot":"","sources":["../../../../shared/soundSnapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,WAAW,aAAa;IAE5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IAGrB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IAGf,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IAGf,WAAW,EAAE,MAAM,CAAC;IAGpB,OAAO,EAAE,MAAM,CAAC;IAGhB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IAGf,MAAM,EAAE,MAAM,CAAC;IACf,CAAC,EAAE,MAAM,CAAC;IAGV,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,aAAa,CAmBxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,EACpB,MAAM,CAAC,EAAE,aAAa,EAAE,EACxB,QAAQ,GAAE,MAAY,EACtB,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,aAAa,CAsEf;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,GAAG,QAAQ,CAAC,aAAa,CAAC,CAEpF"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * SoundSnapshot - Canonical sound data interface for Code Mode integration
3
+ *
4
+ * This is the single source of truth for sound-derived parameters.
5
+ * All values are normalized to 0-100 range for consistency.
6
+ *
7
+ * Usage:
8
+ * - Sound analysis produces SoundSnapshot
9
+ * - Code Mode receives it as read-only S.* globals
10
+ * - SoundArt styles become Code Mode sketches using S.*
11
+ */
12
+ /**
13
+ * Create an empty/default SoundSnapshot with neutral values
14
+ */
15
+ export function createEmptySoundSnapshot() {
16
+ return {
17
+ volume: 50,
18
+ amplitude: 50,
19
+ dynamicRange: 25,
20
+ brightness: 50,
21
+ bass: 30,
22
+ mid: 40,
23
+ treble: 30,
24
+ harmonicity: 50,
25
+ aggression: 30,
26
+ attack: 50,
27
+ rhythmicity: 40,
28
+ silence: 0,
29
+ hue: 50,
30
+ chroma: 50,
31
+ length: 50,
32
+ t: 0,
33
+ };
34
+ }
35
+ /**
36
+ * Clamp a value to 0-100 range
37
+ */
38
+ export function clampPercent(value) {
39
+ return Math.max(0, Math.min(100, value));
40
+ }
41
+ /**
42
+ * Convert a 0-1 normalized value to 0-100 percentage
43
+ */
44
+ export function toPercent(value) {
45
+ return clampPercent(value * 100);
46
+ }
47
+ /**
48
+ * Create a SoundSnapshot from SoundFeatures (analysis output)
49
+ *
50
+ * @param sound - Raw sound features from analysis
51
+ * @param frames - Frame features array
52
+ * @param windowMs - Time window to average (default 800ms for recent snapshot)
53
+ * @param frameIndex - Current frame index for loop mode
54
+ * @param totalFrames - Total frames for loop mode
55
+ */
56
+ export function createSoundSnapshot(sound, frames, windowMs = 800, frameIndex, totalFrames) {
57
+ const framesArr = frames || sound.frames || [];
58
+ const bands = {
59
+ bass: sound.bands?.bass ?? 0.3,
60
+ lowMid: sound.bands?.lowMid ?? 0.25,
61
+ highMid: sound.bands?.highMid ?? 0.25,
62
+ treble: sound.bands?.treble ?? 0.2
63
+ };
64
+ // Get recent frames within window
65
+ const now = framesArr.length > 0
66
+ ? framesArr[framesArr.length - 1].t1 * 1000
67
+ : 0;
68
+ const windowStart = now - windowMs;
69
+ const recentFrames = framesArr.filter(f => f.t1 * 1000 >= windowStart);
70
+ const useFrames = recentFrames.length > 0 ? recentFrames : framesArr;
71
+ // Helper to extract and average frame values
72
+ const getFrameAvg = (key, def) => {
73
+ if (useFrames.length === 0)
74
+ return def;
75
+ const values = useFrames.map(f => f[key] ?? def);
76
+ return values.reduce((a, b) => a + b, 0) / values.length;
77
+ };
78
+ const getFrameSpan = (key, def) => {
79
+ if (useFrames.length < 2)
80
+ return 0;
81
+ const values = useFrames.map(f => f[key] ?? def);
82
+ return Math.max(...values) - Math.min(...values);
83
+ };
84
+ // Extract averaged values
85
+ const rmsNow = getFrameAvg('rms', sound.rms ?? 0);
86
+ const ampNow = getFrameAvg('amplitude', sound.rms ?? 0);
87
+ const attNow = getFrameAvg('attack', 0.5);
88
+ const harmNow = getFrameAvg('harmonicity', 0.5);
89
+ const aggrNow = getFrameAvg('aggression', 0.5);
90
+ const rhNow = getFrameAvg('rhythmicity', 0.4);
91
+ const silNow = getFrameAvg('silence', 0);
92
+ const dynNow = getFrameSpan('rms', sound.rms ?? 0);
93
+ const centNow = getFrameAvg('centroid', sound.centroid ?? 0.5);
94
+ // Apply perceptual scaling for loudness
95
+ const amplifiedRms = Math.pow(rmsNow, 0.33);
96
+ const amplifiedAmp = Math.pow(ampNow, 0.4);
97
+ // Calculate normalized time
98
+ const t = frameIndex !== undefined && totalFrames !== undefined
99
+ ? frameIndex / Math.max(1, totalFrames)
100
+ : (framesArr.length > 0 ? 1 : 0);
101
+ return {
102
+ volume: toPercent(amplifiedRms),
103
+ amplitude: toPercent(amplifiedAmp),
104
+ dynamicRange: toPercent(dynNow),
105
+ brightness: toPercent(centNow),
106
+ bass: toPercent(bands.bass),
107
+ mid: toPercent((bands.lowMid + bands.highMid) / 2),
108
+ treble: toPercent(bands.treble),
109
+ harmonicity: toPercent(harmNow),
110
+ aggression: toPercent(aggrNow),
111
+ attack: toPercent(attNow),
112
+ rhythmicity: toPercent(rhNow),
113
+ silence: toPercent(silNow),
114
+ hue: toPercent(centNow * 0.75 + aggrNow * 0.25),
115
+ chroma: toPercent(Math.min(1, Math.sqrt((bands.bass + bands.treble) / 2))),
116
+ length: Math.min(100, (sound.durationSec ?? 0) * 10),
117
+ t,
118
+ frame: frameIndex,
119
+ totalFrames,
120
+ };
121
+ }
122
+ /**
123
+ * Freeze a SoundSnapshot to make it immutable
124
+ * This is used when injecting into Code Mode to prevent modification
125
+ */
126
+ export function freezeSoundSnapshot(snapshot) {
127
+ return Object.freeze({ ...snapshot });
128
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"static-engine.d.ts","sourceRoot":"","sources":["../static-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAoCtE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Hf"}
1
+ {"version":3,"file":"static-engine.d.ts","sourceRoot":"","sources":["../static-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAkDtE,wBAAsB,aAAa,CACjC,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Hf"}
@@ -15,17 +15,32 @@
15
15
  * When returnImageData is true, returns raw ImageData for determinism hashing.
16
16
  * Works in both browser (HTMLCanvasElement) and Node (canvas package) environments.
17
17
  */
18
- import { createRequire } from 'module';
19
18
  import { DEFAULT_CONFIG } from './types';
20
19
  import { createP5Runtime, injectTimeVariables, injectProtocolVariables } from './p5-runtime';
21
20
  import { FORBIDDEN_APIS, createSafeMath } from './execution-sandbox';
22
- const require = createRequire(import.meta.url);
21
+ let nodeCanvasModule = null;
22
+ async function getNodeCanvas() {
23
+ if (nodeCanvasModule)
24
+ return nodeCanvasModule;
25
+ if (typeof window === 'undefined') {
26
+ try {
27
+ const { createRequire } = await import('module');
28
+ const require = createRequire(import.meta.url);
29
+ nodeCanvasModule = require('canvas');
30
+ return nodeCanvasModule;
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ return null;
37
+ }
23
38
  /**
24
39
  * Create a runtime canvas that works in both browser and Node environments.
25
40
  * Browser: uses HTMLCanvasElement
26
41
  * Node/Headless: uses `canvas` npm package
27
42
  */
28
- function createRuntimeCanvas(width, height) {
43
+ async function createRuntimeCanvas(width, height) {
29
44
  // Browser environment
30
45
  if (typeof document !== 'undefined' && typeof document.createElement === 'function') {
31
46
  const canvas = document.createElement('canvas');
@@ -34,14 +49,12 @@ function createRuntimeCanvas(width, height) {
34
49
  return canvas;
35
50
  }
36
51
  // Node / headless environment (oracle, CI)
37
- try {
38
- const { createCanvas } = require('canvas');
39
- return createCanvas(width, height);
40
- }
41
- catch (err) {
42
- throw new Error('[Code Mode Protocol Error] Headless canvas unavailable. ' +
43
- 'Install `canvas` for oracle execution.');
52
+ const nodeCanvas = await getNodeCanvas();
53
+ if (nodeCanvas && nodeCanvas.createCanvas) {
54
+ return nodeCanvas.createCanvas(width, height);
44
55
  }
56
+ throw new Error('[Code Mode Protocol Error] Headless canvas unavailable. ' +
57
+ 'Install `canvas` for oracle execution.');
45
58
  }
46
59
  export async function runStaticMode(config, options) {
47
60
  const { code, seed, vars, onPreview, onProgress, onComplete, onError, returnImageData } = options;
@@ -54,7 +67,7 @@ export async function runStaticMode(config, options) {
54
67
  message: 'Initializing canvas...',
55
68
  });
56
69
  // Create runtime canvas (browser or Node)
57
- const canvas = createRuntimeCanvas(width, height);
70
+ const canvas = await createRuntimeCanvas(width, height);
58
71
  // Create p5 runtime with optional seed for determinism
59
72
  const p = createP5Runtime(canvas, width, height, { seed });
60
73
  // Inject time variables (static = frame 0, t = 0, totalFrames = 1)