@field/cascade 0.3.0 → 0.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 (75) hide show
  1. package/AGENTS.md +14 -9
  2. package/ARCHITECTURE.md +22 -6
  3. package/CHANGELOG.md +69 -1
  4. package/CONTRIBUTING.md +71 -0
  5. package/DESIGN.md +19 -13
  6. package/README.md +42 -11
  7. package/dist/assets/{CodeEditor-BADFbTJS.js → CodeEditor-BNOJIDzL.js} +4 -4
  8. package/dist/assets/{CodePanel-faK6PRfz.js → CodePanel-2wp2G0Fj.js} +2 -2
  9. package/dist/assets/{CustomNodeDialog-DhlKuviy.js → CustomNodeDialog-DNqaVBqA.js} +1 -1
  10. package/dist/assets/{ProjectSettings-D6a6kjqR.js → ProjectSettings-DcdYF2E1.js} +1 -1
  11. package/dist/assets/{SettingsDialog-DJZdIvLC.js → SettingsDialog-D2xVvyiR.js} +1 -1
  12. package/dist/assets/{VersionHistoryDialog-C1X3HOhc.js → VersionHistoryDialog-DW288ykF.js} +1 -1
  13. package/dist/assets/{core-CiuiuWZi.js → core-B1y38i95.js} +1 -1
  14. package/dist/assets/{cssMode-D-NwZSMJ.js → cssMode-BLucnaln.js} +1 -1
  15. package/dist/assets/{editor.api-DT3WEvBd.js → editor.api-B3Et3Pph.js} +1 -1
  16. package/dist/assets/{freemarker2-eKNYQaiz.js → freemarker2-DVlPoano.js} +1 -1
  17. package/dist/assets/geo-DhyFdBXX.js +1 -0
  18. package/dist/assets/{handlebars-C2w1ggK2.js → handlebars-B5FZBAMR.js} +1 -1
  19. package/dist/assets/{html-DQb41jyo.js → html-FodD8KbR.js} +1 -1
  20. package/dist/assets/{htmlMode-IhNlh6Lv.js → htmlMode-CsG57VCD.js} +1 -1
  21. package/dist/assets/{image-DMCXBY-_.js → image-B3F-nq25.js} +1 -1
  22. package/dist/assets/index-CC6YQw2n.js +212 -0
  23. package/dist/assets/index-Y2ock1cw.css +1 -0
  24. package/dist/assets/{javascript-BIT6jMLC.js → javascript-B3gonF_H.js} +1 -1
  25. package/dist/assets/{jsonMode-CNfyB1ro.js → jsonMode-Y3w1ahZg.js} +1 -1
  26. package/dist/assets/{liquid-CmSmJmOG.js → liquid-CTpWXIYO.js} +1 -1
  27. package/dist/assets/{lspLanguageFeatures-BqMm3uMP.js → lspLanguageFeatures-CODBdKEZ.js} +1 -1
  28. package/dist/assets/{mdx-6r6YgOFw.js → mdx-CDAQJZIz.js} +1 -1
  29. package/dist/assets/{python-x1H0TQuI.js → python-LOIlxJU-.js} +1 -1
  30. package/dist/assets/{razor-Dzl1vZFm.js → razor-CllPQSHo.js} +1 -1
  31. package/dist/assets/{tsMode-n6DMMe3a.js → tsMode-CO28ut8t.js} +1 -1
  32. package/dist/assets/{typescript-ziEz65xN.js → typescript-ruU9wKQ_.js} +1 -1
  33. package/dist/assets/{workers-D-qUdp-r.js → workers-Bc1Ywp4-.js} +1 -1
  34. package/dist/assets/{xml-DMZjt5lE.js → xml-DoXP2Dh1.js} +1 -1
  35. package/dist/assets/{yaml-DFe5qZSO.js → yaml-D9YPyGiJ.js} +1 -1
  36. package/dist/cli/index.js +1240 -158
  37. package/dist/cli/runtime/gpu.ts +147 -0
  38. package/dist/contracts/camera.d.ts +70 -0
  39. package/dist/contracts/camera.js +27 -0
  40. package/dist/contracts/capabilities.d.ts +69 -6
  41. package/dist/contracts/definition.d.ts +61 -2
  42. package/dist/contracts/index.d.ts +2 -0
  43. package/dist/contracts/index.js +2 -0
  44. package/dist/contracts/schema.js +33 -7
  45. package/dist/contracts/series.d.ts +75 -0
  46. package/dist/contracts/series.js +2 -0
  47. package/dist/contracts/validate.js +29 -4
  48. package/dist/contracts/values.d.ts +3 -2
  49. package/dist/contracts/values.js +1 -0
  50. package/dist/gpu.d.ts +88 -0
  51. package/dist/gpu.js +113 -0
  52. package/dist/index.html +2 -2
  53. package/dist/runtime/builtins/core/camera.d.ts +252 -0
  54. package/dist/runtime/builtins/core/camera.js +149 -0
  55. package/dist/runtime/builtins/core/index.d.ts +2 -1
  56. package/dist/runtime/builtins/core/index.js +3 -1
  57. package/dist/runtime/camera/index.d.ts +60 -0
  58. package/dist/runtime/camera/index.js +234 -0
  59. package/dist/runtime/definition/architecture.js +36 -1
  60. package/dist/runtime/gpu.d.ts +33 -0
  61. package/dist/runtime/gpu.js +218 -0
  62. package/dist/runtime/index.d.ts +3 -0
  63. package/dist/runtime/index.js +3 -0
  64. package/dist/runtime/runtime.d.ts +23 -3
  65. package/dist/runtime/runtime.js +181 -30
  66. package/dist/runtime/series/index.d.ts +41 -0
  67. package/dist/runtime/series/index.js +167 -0
  68. package/dist/runtime/types.d.ts +3 -3
  69. package/doc/HEADLESS_GPU.md +160 -0
  70. package/doc/NODE_REFERENCE.md +36 -0
  71. package/doc/PROJECT_AUTHORING.md +106 -20
  72. package/package.json +14 -2
  73. package/dist/assets/geo-DUvVbLnx.js +0 -1
  74. package/dist/assets/index-BDV5xGBZ.css +0 -1
  75. package/dist/assets/index-HTCN44kj.js +0 -212
@@ -0,0 +1,160 @@
1
+ # Headless WebGPU with Dawn
2
+
3
+ The CLI can run definition-v1 nodes declaring `runsOn: 'portable'` or
4
+ `'server'` and `capabilities: ['gpu']` through optional `webgpu@0.6.0` (Dawn).
5
+ Studio uses browser WebGPU through the same device/cache lifecycle mechanism.
6
+ Browser-only declarations remain browser-only; installing Dawn does not supply
7
+ DOM, WebGL, canvas presentation, image decoding, or video APIs.
8
+
9
+ ## Decision and release status
10
+
11
+ Implemented on 2026-09-08 and checked in as source version `0.3.2`; see the
12
+ [changelog](../CHANGELOG.md). Checking in the source does not publish the npm
13
+ package. Use a built checkout or a tarball known to include the feature until
14
+ the corresponding package release is published.
15
+
16
+ The decision is to use optional native Dawn for browser-independent GPU
17
+ execution and agent image feedback. Browser automation remains useful for
18
+ Studio interaction/presentation tests; Skia remains the Canvas2D renderer.
19
+ Neither is replaced by Dawn. This first slice shares WGSL/rendering logic and
20
+ device/cache ownership without introducing a new graph texture pipeline or
21
+ requiring a browser process for production CLI rendering.
22
+
23
+ ## Render for a test or agent
24
+
25
+ From a compatible project, using a Cascade build containing this feature:
26
+
27
+ ```sh
28
+ npx --no-install cascade run index.cascade --frames 1 --json --timeout 60000
29
+ npx --no-install cascade run index.cascade --frames 1-24 --fps 24 --out renders --json --timeout 120000
30
+ ```
31
+
32
+ `--json` requires a fully definition-v1 frame render; use `--frames 1` for a
33
+ still. Stdout contains one success manifest with absolute graph path,
34
+ project-relative image `files`, `frames`, actual `fps`, elapsed `durationMs`,
35
+ and Dawn adapter metadata when a GPU was used. Logs and failure diagnostics go
36
+ to stderr; failures exit nonzero and do not emit a success manifest. This is a
37
+ local command contract, not a chat-provider integration or an untrusted-code sandbox.
38
+
39
+ Illustrative manifest for the `cloud-volumes/dawn.cascade` fixture:
40
+
41
+ ```json
42
+ {
43
+ "status": "completed",
44
+ "graph": "/path/to/cloud-volumes/dawn.cascade",
45
+ "frames": [1, 2],
46
+ "files": ["renders/dawn/volume.0001.png", "renders/dawn/volume.0002.png"],
47
+ "aborted": false,
48
+ "fps": 30,
49
+ "durationMs": 115.1,
50
+ "gpu": {
51
+ "renderer": "dawn",
52
+ "adapter": {
53
+ "vendor": "apple",
54
+ "architecture": "metal-3",
55
+ "device": "apple-m3-max",
56
+ "description": "Metal driver on macOS"
57
+ }
58
+ }
59
+ }
60
+ ```
61
+
62
+ Resolve `files` relative to the directory containing `graph`. `gpu` is omitted
63
+ if no GPU node executed. `durationMs` measures the frame-rendering section and
64
+ cleanup, excluding CLI startup and module preparation; it is not GPU-pass time.
65
+
66
+ For agent callers: set a finite timeout, check the exit code, parse stdout only
67
+ on success, then attach the listed images to the conversation. Keep stderr for
68
+ diagnosis. A manifest records outputs, not a complete reproducibility bundle:
69
+ save the graph, code/assets and authored settings separately. The CLI does not
70
+ upload images or contact a model provider.
71
+
72
+ `--timeout` supervises a separate render process and returns exit 124 when its
73
+ deadline expires. On POSIX, termination covers that process group; on Windows,
74
+ the current supervisor terminates the direct child only. Interrupts are forwarded
75
+ with a short grace period before forced termination. A forced stop can leave
76
+ partial output files; consumers must check the exit code and manifest. Ordinary
77
+ successful runs destroy cached resources and release the native GPU reference.
78
+
79
+ Dawn loads lazily, only when a GPU node executes. Static `check`/`validate`
80
+ inspect the declared host contract without initializing hardware; they do not
81
+ prove that a native binary, driver, or adapter is usable. Missing native support
82
+ produces an installation diagnostic at execution. Dawn itself may be omitted
83
+ for CPU-only environments; retain the compiler's own platform dependencies.
84
+ Skia remains the independent Canvas2D path.
85
+
86
+ ## Authoring contract
87
+
88
+ Use `context.capabilities.gpu.device` and `.cache(key, create, destroy)`.
89
+ Cache keys are scoped per node; destroy callbacks release textures and buffers.
90
+ The application owns the host's disposal, not an individual graph sharing it.
91
+ Use `createRenderPipelineAsync` to surface shader/pipeline validation failures.
92
+ `readTexture` captures validation failures within its own operation. Arbitrary
93
+ GPU commands issued before it need their own error scopes or async validation;
94
+ run-aware routing of all uncaptured device errors is not implemented yet.
95
+
96
+ Render into a single-sample `rgba8unorm` 2D texture with
97
+ `TextureUsage.RENDER_ATTACHMENT | TextureUsage.COPY_SRC`. Then:
98
+
99
+ ```ts
100
+ import { readTexture } from 'cascade/gpu';
101
+
102
+ const pixels = await readTexture(gpu.device, target, { signal: context.signal });
103
+ // pixels: { data: Uint8Array, width, height }, tightly packed top-left RGBA8
104
+ ```
105
+
106
+ Readback strips the WebGPU buffer row padding and copies bytes before unmapping.
107
+ It releases its staging buffer on success, mapping failure, or cancellation.
108
+ It does not encode PNGs or perform colour conversion. Encode through the existing
109
+ image/IO host and publish an `image` value. Graph-wide texture transport, HDR
110
+ readback, and browser video uploads are outside this first implementation.
111
+
112
+ ## Verification and limits
113
+
114
+ ```sh
115
+ CASCADE_TEST_DAWN=1 npx vitest run tests/dawn-native.test.ts
116
+ CASCADE_TEST_DAWN=1 npm run test:package
117
+ ```
118
+
119
+ These opt-in hardware gates fail if Dawn cannot initialize. The first checks
120
+ repeated creation, rendering, padded readback and natural process exit. The
121
+ second installs a packed tarball into an empty project, renders animated PNGs
122
+ through the CLI and checks decoded pixels and the JSON manifest.
123
+ It also removes Dawn from that temporary installation to verify that a CPU
124
+ graph still runs and a GPU graph fails with an actionable missing-renderer
125
+ diagnostic. No silent CPU or browser fallback is performed.
126
+
127
+ Recorded implementation verification (2026-09-08):
128
+
129
+ - Main test suite: 1,322 passed; the opt-in hardware test was skipped there and
130
+ passed separately with `CASCADE_TEST_DAWN=1`.
131
+ - Typechecking: zero errors, eight existing Svelte warnings.
132
+ - Contracts/runtime package checks and tests, server/Studio/CLI builds, and
133
+ packed-package imports/types and rendering checks passed.
134
+ - The real `cloud-volumes/dawn.cascade` fixture passed static checks and wrote
135
+ two distinct animated PNGs with an adapter-bearing JSON manifest.
136
+
137
+ These are recorded results, not a claim that every future checkout or platform
138
+ passes. Rerun the gates after changes; Windows/Linux hardware has not yet been
139
+ verified. Windows process-tree cancellation remains follow-up work.
140
+
141
+ Do not require identical pixels across GPUs/drivers. Pin reference environments,
142
+ record adapter information and use measured image tolerances. Agent callers
143
+ should set a timeout, cap dimensions/frame counts, and preserve graph/code,
144
+ assets, seed and frame settings alongside selected outputs.
145
+
146
+ Initial proof (2026-09-08): the `cloud-volumes` shader at 130 × 96, 32 steps,
147
+ with a fixed opaque 17 × 73 column and 144-byte uniform block produced zero
148
+ changed channels across 49,920 channels for both original browser canvas →
149
+ migrated browser texture and migrated browser → native Dawn. All outputs were
150
+ nonblank. Both hosts used Apple Metal (native: M3 Max). This establishes that
151
+ specific migration's parity, not universal GPU bit-exactness. The sketch's
152
+ `dawn.cascade` additionally exercises the real Aoos input, image IO and animation
153
+ through the CLI at 256 × 320.
154
+
155
+ Native packaging and GPU/driver support vary by platform. The package chooses
156
+ its default backend; Cascade does not enable unsafe Dawn toggles or silently
157
+ configure software drivers. Software Vulkan requires separate host setup.
158
+ See [Dawn Node bindings](https://github.com/dawn-gpu/node-webgpu),
159
+ [software backend setup](https://dawn.googlesource.com/dawn/+/refs/heads/main/src/dawn/node/README.md),
160
+ and [WGSL floating-point rules](https://gpuweb.github.io/gpuweb/wgsl/#floating-point-evaluation).
@@ -17,6 +17,42 @@ node and none is needed.
17
17
  These are definition-v1: their declarations are literal, so what follows is
18
18
  exact rather than a description.
19
19
 
20
+ ### `cascade.core.Camera`
21
+
22
+ Camera — A camera, following Houdini's /obj/cam.
23
+
24
+ Runs on `portable`.
25
+
26
+ **Inputs**
27
+
28
+ | name | type | default | range |
29
+ | --- | --- | --- | --- |
30
+ | `translate` | `vec3` | `[0,0,5]` | |
31
+ | `rotate` | `vec3` | `[0,0,0]` | |
32
+ | `focal` | `float` | `50` | 1…300 |
33
+ | `lookat` | `vec3` | `[0,0,0]` | |
34
+ | `up` | `vec3` | `[0,1,0]` | |
35
+ | `spin` | `float` | `0` | |
36
+
37
+ **Props**
38
+
39
+ | name | type | default | range |
40
+ | --- | --- | --- | --- |
41
+ | `lookAt` | `bool` | `false` | |
42
+ | `aperture` | `float` | `41.4214` | 1…200 |
43
+ | `resolution` | `vec2i` | `[1280,720]` | |
44
+ | `aspect` | `float` | `1` | 0.1…10 |
45
+ | `projection` | `string` | `"perspective"` | |
46
+ | `orthowidth` | `float` | `2` | |
47
+ | `near` | `float` | `0.001` | |
48
+ | `far` | `float` | `10000` | |
49
+
50
+ **Outputs**
51
+
52
+ | name | type | default | range |
53
+ | --- | --- | --- | --- |
54
+ | `camera` | `camera` | | |
55
+
20
56
  ### `cascade.core.Input`
21
57
 
22
58
  Input — Defines a public graph or subnet input
@@ -4,10 +4,15 @@ Cascade projects are filesystem-native. The graph, custom nodes, assets, presets
4
4
 
5
5
  ## Create and open a project
6
6
 
7
+ `cascade new` scaffolds and starts Studio. Stop that initial server before
8
+ installing dependencies and reopening the project. This example assumes the
9
+ default projects directory; use `cascade projects` to check your configured path.
10
+
7
11
  ```bash
8
12
  cascade new my-artwork
9
13
  cd ~/Documents/Cascade/my-artwork
10
- cascade .
14
+ npm install
15
+ npx --no-install cascade .
11
16
  ```
12
17
 
13
18
  For a workstation reached by name over a trusted VPN or LAN, bind deliberately
@@ -21,10 +26,41 @@ Open `http://KURO:3030`. Loopback remains the default. Cascade trusts the
21
26
  specific non-loopback bind hostname; `--trusted-host` is only needed when the
22
27
  browser uses a different hostname. Wildcard remote binds are rejected.
23
28
 
29
+ For an HTTPS reverse proxy that terminates TLS and forwards to loopback, bind
30
+ loopback explicitly and allow the exact external hostname:
31
+
32
+ ```bash
33
+ cascade . --host 127.0.0.1 --port 3030 --trusted-host cascade.example.internal
34
+ ```
35
+
36
+ The proxy must preserve that `Host` value; include a non-default external port
37
+ in `--trusted-host` when applicable. Add `--no-open` and visit the HTTPS URL
38
+ yourself, since automatic opening still constructs an HTTP URL. Cascade does not configure the
39
+ proxy or certificate; the explicit trusted host only admits its browser
40
+ authority while the server remains unreachable directly from the network.
41
+
24
42
  Cascade and generated projects require Node.js 22.13 or newer. Generated
25
43
  projects use TypeScript 6 so their static node definitions match Cascade's
26
44
  compiler and definition extractor.
27
45
 
46
+ `cascade new` writes a project-local dependency named `cascade` which aliases
47
+ the published `@field/cascade` package. Install it before using the project's
48
+ npm scripts:
49
+
50
+ ```bash
51
+ npm install
52
+ npm run check
53
+ npm run validate
54
+ ```
55
+
56
+ That dependency uses a caret range in the CLI's current minor line; the lockfile
57
+ records the resolved version. A local Cascade repository may contain newer
58
+ work even with the same version number. Record both the version and whether
59
+ the project resolves a checkout or a published package when diagnosing a
60
+ difference. Use a deliberate local package override
61
+ for framework development rather than committing a machine-specific
62
+ `file:../../Dev/cascade` dependency to a shared project.
63
+
28
64
  `index.cascade` is the default graph name. A project may contain several `.cascade` files; pass one explicitly when there is no unambiguous default:
29
65
 
30
66
  ```bash
@@ -94,13 +130,15 @@ export const renderers = {
94
130
 
95
131
  Each renderer instance owns its cleanup. An optional `update` method receives later values without remounting.
96
132
 
97
- To open a panel from the Inspector, declare an action parameter:
133
+ To open a panel from the Inspector, add an action prop to a definition-v1 node:
98
134
 
99
135
  ```ts
100
- node.param('asset_id', '', {
101
- label: 'Choose asset',
102
- action: 'panel:asset-browser',
103
- });
136
+ props: {
137
+ asset_id: {
138
+ type: 'string', default: '', label: 'Choose asset',
139
+ action: 'panel:asset-browser'
140
+ }
141
+ }
104
142
  ```
105
143
 
106
144
  The initiating node ID is captured as `api.sourceNodeId`; `setParam` uses the same history/dirty/cook path as the Inspector, so undo and save continue to work. Panels and project value renderers are Studio-only and are never imported during `cascade run` or by `cascade/runtime`.
@@ -133,11 +171,12 @@ const response = await authorizedFetch('image-api', 'https://api.example.com/v1/
133
171
  });
134
172
  ```
135
173
 
136
- `cascade/net` is resolved by Cascade's project compiler and backed by the
137
- Studio server's `/api/net` route. It is not a published package export or a
138
- neutral headless runtime capability. A standalone headless host must provide
139
- its own remote-I/O adapter or run the integration through an injected shell,
140
- Python, or application-specific server bridge.
174
+ `cascade/net` is a published host bridge backed by the Studio server's
175
+ `/api/net` route when project code is compiled by Cascade. It is not a neutral
176
+ runtime capability and has no useful authority without that installed bridge.
177
+ A standalone headless host must provide its own remote-I/O adapter or run the
178
+ integration through an injected shell, Python, or application-specific server
179
+ bridge.
141
180
 
142
181
  Browser proxy credentials must declare the exact allowed hosts and header metadata alongside the secret:
143
182
 
@@ -160,10 +199,18 @@ There are two styles. Write **definition-v1** for anything new; the dynamic styl
160
199
 
161
200
  | | Cooked by | Checked by |
162
201
  | --- | --- | --- |
163
- | **definition-v1**: a literal `definition` plus `execute(context)` | the deterministic runtime through `cascade run`, and Studio, which builds the ports from the literal | `cascade check`, which requires this style |
164
- | **dynamic**: `execute(node, graph)` declaring its own ports and parameters | Studio, and `cascade run` including `--frames` | `cascade validate` and a cook |
165
-
166
- Studio cooks both, and a Studio graph may mix them. `cascade run` picks one host per document: a graph whose project modules are all definition-v1 runs through the deterministic runtime, and anything else runs through the compatibility engine — which is also why `--frames` is refused on an all-definition-v1 graph, the deterministic runtime owning its own clock. The `AGENTS.md` that `cascade new` writes into a project states which style that project expects, and it is the file to believe over this one for a specific project.
202
+ | **definition-v1**: a literal `definition` plus `execute(context)` | the deterministic runtime through `cascade run`, including `--frames`; Studio adapts it into its compatibility controller | `cascade check`, which requires this style |
203
+ | **dynamic**: `execute(node, graph)` declaring its own ports and parameters | Studio and the CLI compatibility engine, including `--frames` | `cascade validate` and a cook |
204
+
205
+ Studio cooks both, and a Studio graph may mix them. `cascade run` picks one host
206
+ per document: an all-definition-v1 graph runs through the deterministic
207
+ runtime and an all-dynamic graph runs through the compatibility engine. A
208
+ mixed CLI document is rejected until the bounded adapter between those engines
209
+ exists. Both homogeneous forms support `--frames`; deterministic runs receive
210
+ an explicit frame and fps so expressions and keyframe channels resolve
211
+ reproducibly. The `AGENTS.md` that `cascade new` writes into a project states
212
+ which style that project expects, and it is the file to believe over this one
213
+ for a specific project.
167
214
 
168
215
  ### definition-v1
169
216
 
@@ -244,9 +291,9 @@ export async function execute(node: any) {
244
291
  }
245
292
  ```
246
293
 
247
- `param()` is a method on the node the engine hands to `execute`, not an import. It is re-declared on every cook, so it never resets a value, an expression, or a keyframe someone set: reading `.value` gives the *resolved* value for the current frame, whichever of the three bindings is in force. Top-level statements outside `execute` do not run, because the loader imports a real ES module and needs the `execute` export.
294
+ `param()` is a method on the node the engine hands to `execute`, not an import. It is re-declared on every cook, so it never resets a value, an expression, or a keyframe someone set: reading `.value` gives the *resolved* value for the current frame, whichever of the three bindings is in force. The loader imports the module as real JavaScript, so top-level code does run at import time. Keep dynamic modules free of top-level effects and put interface declaration and cooking inside `execute`.
248
295
 
249
- Prefer `new OffscreenCanvas(width, height)` to `document.createElement('canvas')` when both hosts could run the node, and keep `runsOn: 'browser'` for code that genuinely needs the page. Web technology comes first, meaning Canvas 2D, WebGL and WebGPU. A Python stage is for work that cannot run in a browser at all, such as an exotic ML library. A Python stage costs a process spawn and a round trip per cook, which is invisible on one still frame and fatal to dragging a parameter or playing an animation.
296
+ Prefer `new OffscreenCanvas(width, height)` to `document.createElement('canvas')` when both hosts can run the node. A portable declaration still requires output parity checks; installing Skia does not override a browser-only declaration. Use Python for libraries or tools that require a server. Measure transport, serialization, and cold-start costs; persistent workers can avoid repeated process/model startup.
250
297
 
251
298
  Choose the narrowest execution locus:
252
299
 
@@ -268,6 +315,25 @@ export const definition = {
268
315
 
269
316
  The runtime refuses unsupported host/capability combinations before executing the graph.
270
317
 
318
+ ### GPU nodes today
319
+
320
+ A definition-v1 node may declare `capabilities: ['gpu']`. Studio and the optional
321
+ Dawn CLI host provide one shared `GPUDevice`, adapter identity and limits, plus
322
+ a cache scoped to each node instance for pipelines, samplers, and other
323
+ resources that outlive one cook. The host clears cached resources on host
324
+ disposal or device loss and requests a fresh device on a later cook.
325
+
326
+ Use `runsOn: 'portable'` for shared WebGPU code without browser APIs. Render into
327
+ a texture and use `readTexture` from `cascade/gpu` for explicit RGBA8 pixels,
328
+ then encode through the image/IO host. See [headless GPU rendering](HEADLESS_GPU.md)
329
+ for CLI/agent commands, restrictions and tests.
330
+
331
+ This is the first GPU capability stage. Texture connections are not available:
332
+ GPU nodes still publish images, so an image must be encoded or read back before
333
+ another node receives it. Do not model a current graph as a chain of portable
334
+ texture handles, and do not keep a device or pipeline in module-level mutable
335
+ state.
336
+
271
337
  Some portable operations have genuinely useful browser and server
272
338
  implementations. Keep one literal definition and one module ID; let the host's
273
339
  registration load the implementation for its environment. Do not add an
@@ -340,7 +406,11 @@ In the document, a channel sits in the node's `props` next to the value and the
340
406
  }
341
407
  ```
342
408
 
343
- `params` holds plain values; a binding needs the object form under `props`. Playback settings, the frame range and loop, belong to the Timeline panel and are not saved with the document.
409
+ Definition-v1 parameter values belong under `props`: use a bare value for an
410
+ unbound parameter or the object form for an expression/channel. A leftover
411
+ `params` array is not read by the deterministic runtime and produces a
412
+ `runtime/stray-params` warning. Playback settings, frame range, and loop belong
413
+ to the Timeline panel and are not saved with the document.
344
414
 
345
415
  ## Run a coding agent in the project
346
416
 
@@ -484,12 +554,23 @@ cascade run index.cascade --frames 42 --entry-node logo-1024
484
554
 
485
555
  Offline, drawing goes through Skia: the optional `@napi-rs/canvas` supplies `OffscreenCanvas`, `Image`, `Path2D` and the rest as globals in the Node process, deliberately not `document`, because that is how a node tells which host it is in. This is why a node that draws on an `OffscreenCanvas` needs no second implementation for headless runs, and why the built-in `cascade.image.*` nodes render under `cascade run` as well as in Studio. A graph that draws nothing runs without the renderer installed.
486
556
 
487
- `--frames` renders dynamic graphs. A definition-v1 graph runs through the deterministic runtime, which owns its own clock and has no parameter animation, and the flag is refused rather than silently rendering the first frame repeatedly.
557
+ `--frames` renders both homogeneous graph forms. An all-definition-v1 graph
558
+ runs once per requested frame through the deterministic runtime, which resolves
559
+ expressions and channels from the explicit frame and fps. An all-dynamic graph
560
+ uses the compatibility frame renderer. A mixed graph is rejected rather than
561
+ silently choosing one engine.
488
562
 
489
563
  Applications can use `createRuntime()` from `cascade/runtime` directly. Register node modules and host capabilities, load the graph, mutate inputs or presets, run or trigger it, read outputs, then dispose it. This path loads no Svelte or Studio code and works in a backend service or a custom browser application.
490
564
 
491
565
  Server and browser hosts are deliberately explicit. A graph that uses Python or shell stages belongs on the server or behind an application-defined bridge; the runtime does not silently move computation between environments.
492
566
 
567
+ The Studio single-HTML/folder exporter is not this embedding API. It currently
568
+ serializes a compatibility graph into its own small browser runtime, so it does
569
+ not preserve every node, capability, or runtime behavior. Use it only after
570
+ testing the exported artifact for the particular graph. For a dependable
571
+ custom experience today, embed `cascade/runtime` in an application and provide
572
+ the graph's declared modules and capabilities explicitly.
573
+
493
574
  ## Verify a project
494
575
 
495
576
  Before committing a custom node or graph:
@@ -499,4 +580,9 @@ Before committing a custom node or graph:
499
580
  3. Run the graph headlessly when its capabilities are available. This is the step that catches a document that loads, reports its nodes, and draws nothing. The characteristic failure here is silent, so a static pass alone proves little.
500
581
  4. Open Studio only for visual inspection or authoring that benefits from the graph UI.
501
582
 
502
- Dynamic modules are not deprecated and 0.3.0 stopped calling them legacy: the two styles are cooked by different hosts, and a project's own `AGENTS.md` says which one it expects. Convert a node when you need what the other style gives, static inspection and `cascade check` on one side or Studio cooking and `--frames` on the other, rather than on a schedule.
583
+ Dynamic modules are not deprecated and 0.3.0 stopped calling them legacy. The
584
+ two styles are cooked by different CLI engines, while Studio currently adapts
585
+ both through its compatibility controller. Prefer definition-v1 for new work
586
+ because it provides static inspection, `cascade check`, and the neutral runtime.
587
+ Keep an existing dynamic node when its current compatibility-only APIs are
588
+ still required, and keep CLI graphs homogeneous until mixed execution exists.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@field/cascade",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Visual programming for generative art",
5
5
  "license": "MIT",
6
6
  "author": "FIELD.IO <marcus@field.io>",
@@ -27,11 +27,13 @@
27
27
  "dist",
28
28
  "!dist/**/*.map",
29
29
  "CHANGELOG.md",
30
+ "CONTRIBUTING.md",
30
31
  "AGENTS.md",
31
32
  "ARCHITECTURE.md",
32
33
  "DESIGN.md",
33
34
  "doc/NODE_REFERENCE.md",
34
35
  "doc/PROJECT_AUTHORING.md",
36
+ "doc/HEADLESS_GPU.md",
35
37
  "README.md",
36
38
  "LICENSE"
37
39
  ],
@@ -71,6 +73,10 @@
71
73
  "types": "./dist/runtime/animation/index.d.ts",
72
74
  "default": "./dist/runtime/animation/index.js"
73
75
  },
76
+ "./runtime/camera": {
77
+ "types": "./dist/runtime/camera/index.d.ts",
78
+ "default": "./dist/runtime/camera/index.js"
79
+ },
74
80
  "./runtime/definition/extract": {
75
81
  "types": "./dist/runtime/definition/extract.d.ts",
76
82
  "default": "./dist/runtime/definition/extract.js"
@@ -83,6 +89,10 @@
83
89
  "types": "./dist/io.d.ts",
84
90
  "default": "./dist/io.js"
85
91
  },
92
+ "./gpu": {
93
+ "types": "./dist/gpu.d.ts",
94
+ "default": "./dist/gpu.js"
95
+ },
86
96
  "./net": {
87
97
  "types": "./dist/net.d.ts",
88
98
  "default": "./dist/net.js"
@@ -104,6 +114,7 @@
104
114
  "check:packages": "npm run check --workspace @cascade/contracts && npm run build --workspace @cascade/contracts && npm run check --workspace @cascade/runtime",
105
115
  "test:packages": "npm run test --workspace @cascade/contracts && npm run test --workspace @cascade/runtime",
106
116
  "build:cli": "npm run build:packages && node scripts/build-cli.mjs && npm run build:node-reference",
117
+ "build:server": "npm --prefix server run build",
107
118
  "prepack": "npm run build",
108
119
  "preview": "vite preview",
109
120
  "check": "svelte-check --tsconfig ./tsconfig.json",
@@ -144,6 +155,7 @@
144
155
  "dompurify": "$dompurify"
145
156
  },
146
157
  "optionalDependencies": {
147
- "@napi-rs/canvas": "^1.0.8"
158
+ "@napi-rs/canvas": "^1.0.8",
159
+ "webgpu": "0.6.0"
148
160
  }
149
161
  }
@@ -1 +0,0 @@
1
- import{b as e,f as t,s as n,x as r}from"./index-HTCN44kj.js";r(n,{assets:e});export{t as geoLibrary};