@call-me-sensei/toonlab 0.2.0 → 0.3.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 (205) hide show
  1. package/AGENTS.md +127 -4
  2. package/ATTRIBUTION.md +41 -3
  3. package/README.md +234 -34
  4. package/docs/characters.md +144 -0
  5. package/docs/debug-panel.md +126 -0
  6. package/docs/docs.css +589 -0
  7. package/docs/environment.md +186 -0
  8. package/docs/getting-started.md +180 -0
  9. package/docs/index.html +14 -0
  10. package/docs/lab-architecture.md +100 -0
  11. package/docs/lighting.md +825 -0
  12. package/docs/main.jsx +739 -0
  13. package/docs/mcp.md +97 -0
  14. package/docs/post-processing.md +98 -0
  15. package/docs/settings-reference.md +1878 -0
  16. package/docs/shader-constants.md +77 -0
  17. package/docs/sky.md +182 -0
  18. package/docs/style-labs.md +309 -0
  19. package/docs/texture-lab.md +135 -0
  20. package/docs/toon-shading.md +183 -0
  21. package/docs/tsl-conventions.md +167 -0
  22. package/docs/vegetation-sky.md +275 -0
  23. package/docs/water.md +430 -0
  24. package/docs/weather.md +200 -0
  25. package/docs/world-scale.md +111 -0
  26. package/mcp/server.mjs +610 -0
  27. package/mcp/style-lab-tools.mjs +371 -0
  28. package/mcp/vite-plugin.mjs +174 -0
  29. package/mcp/workspace.mjs +397 -0
  30. package/package.json +64 -5
  31. package/src/ambientfx/INTEGRATION.md +164 -0
  32. package/src/ambientfx/ambientFxPresets.js +83 -0
  33. package/src/ambientfx/ambientFxSettings.js +368 -0
  34. package/src/ambientfx/emitters.js +169 -0
  35. package/src/ambientfx/index.js +5 -0
  36. package/src/ambientfx/particleBackbone.js +493 -0
  37. package/src/ambientfx/stylizedAmbientFx.js +450 -0
  38. package/src/assetlib/ambientcg.js +117 -0
  39. package/src/assetlib/assetRef.js +91 -0
  40. package/src/assetlib/importedEntry.js +40 -0
  41. package/src/assetlib/index.js +23 -0
  42. package/src/assetlib/kaykit.js +192 -0
  43. package/src/assetlib/kaykitStaticIndex.js +700 -0
  44. package/src/assetlib/loadImported.js +143 -0
  45. package/src/assetlib/opensource3d.js +113 -0
  46. package/src/assetlib/polyhaven.js +182 -0
  47. package/src/assetlib/polypizza.js +115 -0
  48. package/src/assetlib/smithsonian.js +214 -0
  49. package/src/assetlib/sources.js +279 -0
  50. package/src/assetlib/zip.js +58 -0
  51. package/src/biome/biomeGenerator.js +385 -0
  52. package/src/biome/biomeRuntime.js +299 -0
  53. package/src/biome/index.js +2 -0
  54. package/src/buildinggen/buildingAsset.js +44 -0
  55. package/src/buildinggen/buildingGrammar.js +311 -0
  56. package/src/buildinggen/buildingMesh.js +451 -0
  57. package/src/buildinggen/buildingPresets.js +46 -0
  58. package/src/buildinggen/buildingRecipe.js +100 -0
  59. package/src/buildinggen/buildingSettings.js +238 -0
  60. package/src/buildinggen/index.js +6 -0
  61. package/src/camera/cameraDirector.js +157 -0
  62. package/src/camera/cameraGenerator.js +367 -0
  63. package/src/camera/cameraRig.js +570 -0
  64. package/src/camera/cameraSettings.js +236 -0
  65. package/src/camera/index.js +7 -0
  66. package/src/catalog/builtinEntries.js +245 -0
  67. package/src/catalog/catalog.js +210 -0
  68. package/src/catalog/index.js +3 -0
  69. package/src/catalog/manifest.js +84 -0
  70. package/src/core/generation.js +529 -0
  71. package/src/environment/environmentRigs.js +21 -1
  72. package/src/environment/environmentSettings.js +4 -0
  73. package/src/environment/environmentSunShadowPass.js +8 -0
  74. package/src/fauna/INTEGRATION.md +174 -0
  75. package/src/fauna/boids.js +861 -0
  76. package/src/fauna/faunaBodies.js +492 -0
  77. package/src/fauna/faunaPresets.js +52 -0
  78. package/src/fauna/faunaSettings.js +525 -0
  79. package/src/fauna/index.js +5 -0
  80. package/src/fauna/stylizedFauna.js +395 -0
  81. package/src/game-feel/gameFeelGenerator.js +402 -0
  82. package/src/game-feel/gameFeelRuntime.js +549 -0
  83. package/src/game-feel/index.js +2 -0
  84. package/src/index.js +17 -6
  85. package/src/lighting/colorIntensity.js +177 -0
  86. package/src/lighting/index.js +161 -0
  87. package/src/lighting/lightDescriptors.js +249 -0
  88. package/src/lighting/lightingCapabilities.js +79 -0
  89. package/src/lighting/lightingDocuments.js +247 -0
  90. package/src/lighting/lightingFixtures.js +446 -0
  91. package/src/lighting/lightingGenerator.js +449 -0
  92. package/src/lighting/lightingPresets.js +319 -0
  93. package/src/lighting/lightingRuntime.js +723 -0
  94. package/src/lighting/lightingStyle.js +386 -0
  95. package/src/lighting/lightingSystem.js +774 -0
  96. package/src/lighting/unrealExport.js +186 -0
  97. package/src/lighting/utils.js +87 -0
  98. package/src/motion/index.js +5 -0
  99. package/src/motion/motionClip.js +441 -0
  100. package/src/motion/motionController.js +628 -0
  101. package/src/motion/motionDocuments.js +225 -0
  102. package/src/motion/motionGraph.js +307 -0
  103. package/src/motion/motionSettings.js +222 -0
  104. package/src/pathgen/index.js +7 -0
  105. package/src/pathgen/pathBridge.js +232 -0
  106. package/src/pathgen/pathPresets.js +35 -0
  107. package/src/pathgen/pathRibbon.js +410 -0
  108. package/src/pathgen/pathRouter.js +380 -0
  109. package/src/pathgen/pathSettings.js +335 -0
  110. package/src/pathgen/pathTextures.js +123 -0
  111. package/src/pathgen/stylizedPaths.js +453 -0
  112. package/src/post/index.js +1 -0
  113. package/src/post/postGenerator.js +177 -0
  114. package/src/post/postProcessing.js +41 -0
  115. package/src/propgen/generatorsWave1.js +379 -0
  116. package/src/propgen/generatorsWave2.js +462 -0
  117. package/src/propgen/index.js +5 -0
  118. package/src/propgen/propAsset.js +323 -0
  119. package/src/propgen/propParts.js +170 -0
  120. package/src/propgen/propPlacement.js +459 -0
  121. package/src/propgen/propPresets.js +82 -0
  122. package/src/propgen/propSettings.js +395 -0
  123. package/src/shaders-tsl/chunks/projected-water-caustics.js +242 -0
  124. package/src/shaders-tsl/chunks/vegetation-style.js +360 -0
  125. package/src/shaders-tsl/chunks/water-shore-state.js +31 -0
  126. package/src/shaders-tsl/chunks/water-waves.js +90 -11
  127. package/src/shaders-tsl/environment.js +15 -1
  128. package/src/shaders-tsl/flower.js +279 -30
  129. package/src/shaders-tsl/grass.js +60 -33
  130. package/src/shaders-tsl/sky.js +125 -31
  131. package/src/shaders-tsl/tree-leaf.js +61 -25
  132. package/src/shaders-tsl/water-breaker.js +7 -4
  133. package/src/shaders-tsl/water-shore-state-simulation.js +523 -0
  134. package/src/shaders-tsl/water.js +439 -49
  135. package/src/shaders-tsl/woody-surface.js +154 -0
  136. package/src/sky/sceneOverrideLayers.js +10 -0
  137. package/src/sky/skyQuality.js +26 -0
  138. package/src/sky/stylizedSky.js +753 -45
  139. package/src/soundscape/index.js +4 -0
  140. package/src/soundscape/soundscapeGenerator.js +179 -0
  141. package/src/soundscape/soundscapeRuntime.js +806 -0
  142. package/src/soundscape/soundscapeSettings.js +292 -0
  143. package/src/styles/index.js +13 -0
  144. package/src/styles/styleBundle.js +325 -0
  145. package/src/stylizedTerrain.js +32 -2
  146. package/src/stylizedWorld.js +423 -20
  147. package/src/texgen/evaluateTexture.js +675 -0
  148. package/src/texgen/index.js +60 -0
  149. package/src/texgen/noise2.js +210 -0
  150. package/src/texgen/textureAi.js +436 -0
  151. package/src/texgen/textureGenerators.js +516 -0
  152. package/src/texgen/texturePresets.js +490 -0
  153. package/src/texgen/textureSettings.js +342 -0
  154. package/src/texgen/textureThree.js +59 -0
  155. package/src/vegetation/flowerSpecies.js +15 -3
  156. package/src/vegetation/grassPalettes.js +153 -0
  157. package/src/vegetation/index.js +6 -0
  158. package/src/vegetation/stylizedBush.js +2 -0
  159. package/src/vegetation/stylizedFlower.js +82 -0
  160. package/src/vegetation/stylizedFlowers.js +48 -7
  161. package/src/vegetation/stylizedForest.js +29 -1
  162. package/src/vegetation/stylizedGrass.js +291 -56
  163. package/src/vegetation/stylizedTree.js +38 -2
  164. package/src/vegetation/stylizedTreeFoliage.js +2 -1
  165. package/src/vegetation/vegetationShaders.js +1110 -0
  166. package/src/vfxgen/INTEGRATION.md +145 -0
  167. package/src/vfxgen/core/burstBackbone.js +380 -0
  168. package/src/vfxgen/core/projectileCore.js +92 -0
  169. package/src/vfxgen/core/spriteShapes.js +98 -0
  170. package/src/vfxgen/core/trailRibbon.js +272 -0
  171. package/src/vfxgen/core/vfxRandom.js +29 -0
  172. package/src/vfxgen/effects/emitHelpers.js +37 -0
  173. package/src/vfxgen/effects/magicEffects.js +162 -0
  174. package/src/vfxgen/effects/movementEffects.js +87 -0
  175. package/src/vfxgen/effects/weaponEffects.js +118 -0
  176. package/src/vfxgen/index.js +18 -0
  177. package/src/vfxgen/moves/moveController.js +146 -0
  178. package/src/vfxgen/moves/moveLibrary.js +335 -0
  179. package/src/vfxgen/vfxPresets.js +98 -0
  180. package/src/vfxgen/vfxSettings.js +384 -0
  181. package/src/vfxgen/vfxSystem.js +449 -0
  182. package/src/vfxgen/weapons/stylizedWeapons.js +137 -0
  183. package/src/villagegen/index.js +4 -0
  184. package/src/villagegen/stylizedVillage.js +490 -0
  185. package/src/villagegen/villageArchetypes.js +160 -0
  186. package/src/villagegen/villageNames.js +40 -0
  187. package/src/villagegen/villageSites.js +105 -0
  188. package/src/water/sceneOverrideLayers.js +23 -0
  189. package/src/water/water.js +5 -0
  190. package/src/water/waterBreakerSystem.js +15 -1
  191. package/src/water/waterCurrentField.js +447 -0
  192. package/src/water/waterMaterial.js +12 -0
  193. package/src/water/waterNearshorePhase.js +320 -0
  194. package/src/water/waterScenePasses.js +83 -30
  195. package/src/water/waterSettings.js +325 -13
  196. package/src/water/waterShoreMaterial.js +322 -0
  197. package/src/water/waterShoreStateField.js +605 -0
  198. package/src/water/waterSurface.js +797 -28
  199. package/src/weather/index.js +6 -0
  200. package/src/weather/weatherPrecipitation.js +221 -0
  201. package/src/weather/weatherPresets.js +258 -0
  202. package/src/weather/weatherSettings.js +269 -0
  203. package/src/weather/weatherSystem.js +871 -0
  204. package/src/worldMinimap.js +62 -0
  205. package/src/worldPresets.js +4 -1
package/docs/main.jsx ADDED
@@ -0,0 +1,739 @@
1
+ // OSS documentation pages. Everything documented here works offline with the
2
+ // open-source package; hosted-only features are explicitly badged "Pro" and
3
+ // link to toonlab.io. The settings reference renders the generated
4
+ // settings-reference.md from this folder.
5
+ import { useEffect, useMemo, useState } from 'react';
6
+ import { createRoot } from 'react-dom/client';
7
+ import { marked } from 'marked';
8
+ import {
9
+ BookOpenText,
10
+ Bot,
11
+ Check,
12
+ Copy,
13
+ Crown,
14
+ Lightbulb,
15
+ Plug,
16
+ Sparkles,
17
+ TerminalSquare,
18
+ } from 'lucide-react';
19
+ import settingsReferenceRaw from './settings-reference.md?raw';
20
+ import './docs.css';
21
+
22
+ const GITHUB = 'https://github.com/call-me-sensei/toonlab';
23
+
24
+ function CopyButton({ value }) {
25
+ const [copied, setCopied] = useState(false);
26
+ return (
27
+ <button
28
+ type="button"
29
+ className="docs-codeblock-copy"
30
+ onClick={async () => {
31
+ await navigator.clipboard.writeText(value);
32
+ setCopied(true);
33
+ setTimeout(() => setCopied(false), 1600);
34
+ }}
35
+ >
36
+ {copied ? <Check size={13} aria-hidden /> : <Copy size={13} aria-hidden />}
37
+ {copied ? 'Copied' : 'Copy'}
38
+ </button>
39
+ );
40
+ }
41
+
42
+ function CodeBlock({ code, label }) {
43
+ return (
44
+ <div className="docs-codeblock">
45
+ {label && <div className="docs-codeblock-label">{label}</div>}
46
+ <pre><code>{code}</code></pre>
47
+ <CopyButton value={code} />
48
+ </div>
49
+ );
50
+ }
51
+
52
+ function PromptBlock({ title, prompt, note, pro }) {
53
+ return (
54
+ <div className="docs-promptblock">
55
+ <span className="docs-promptblock-title">
56
+ {title}
57
+ {pro && <span className="docs-pro">Pro</span>}
58
+ </span>
59
+ <CodeBlock code={prompt} />
60
+ {note && <p className="docs-promptblock-note">{note}</p>}
61
+ </div>
62
+ );
63
+ }
64
+
65
+ function ProBadge() {
66
+ return <span className="docs-pro">Pro</span>;
67
+ }
68
+
69
+ /* ---------------------------------------------------------------- overview */
70
+
71
+ function Overview() {
72
+ return (
73
+ <article>
74
+ <div className="docs-eyebrow">Documentation</div>
75
+ <h1>Build games with ToonLab and your AI coding agent</h1>
76
+ <p className="docs-lede">
77
+ ToonLab is an open-source anime-style runtime for Three.js plus browser labs for authoring
78
+ shaders, assets, and world systems. Everything on this page runs from this repo or the npm
79
+ package — no account, no service. The optimal workflow adds an AI coding agent driving it
80
+ all through skills and MCP.
81
+ </p>
82
+
83
+ <h2>The pieces</h2>
84
+ <ul>
85
+ <li>
86
+ <strong>The runtime library</strong> —{' '}
87
+ <a href="https://www.npmjs.com/package/@call-me-sensei/toonlab" target="_blank" rel="noreferrer">
88
+ <code>@call-me-sensei/toonlab</code>
89
+ </a>
90
+ : toon and environment shading, water, sky, weather, vegetation, lighting, post, camera,
91
+ game feel, and seeded generators for rocks, props, buildings, paths, and villages.
92
+ MIT-licensed, WebGPU-first with a WebGL2 fallback, zero bundled textures.
93
+ </li>
94
+ <li>
95
+ <strong>The labs</strong> — visual editors served by <code>npm run dev</code> (or hosted
96
+ at <a href="https://toonlab.io" target="_blank" rel="noreferrer">toonlab.io</a>). Every
97
+ lab exports a portable preset document the runtime loads.
98
+ </li>
99
+ <li>
100
+ <strong>The local MCP server</strong> — bundled with the package: your coding agent
101
+ searches the procedural catalog and public CC0 sources, reads and saves presets, and
102
+ imports assets into an on-disk <code>.toonlab/</code> workspace. Stdio, no OAuth.
103
+ </li>
104
+ <li>
105
+ <strong>Hosted generation and library</strong> <ProBadge /> — toonlab.io adds a remote
106
+ MCP server with AI generation (concept art, seamless textures, image→3D), stored
107
+ characters, a cloud library, and the public gallery.
108
+ </li>
109
+ </ul>
110
+
111
+ <h2>The optimal workflow</h2>
112
+ <p>
113
+ <span className="docs-step-label">01</span>
114
+ <strong>Install the runtime and the agent skills.</strong> <code>npm install
115
+ @call-me-sensei/toonlab</code>, then copy the feature skills from{' '}
116
+ <a href={`${GITHUB}/tree/main/agents`} target="_blank" rel="noreferrer"><code>agents/</code></a>{' '}
117
+ into your project — they teach the agent the assembly order, the frame-loop contract, and
118
+ each subsystem&apos;s API. Start with <code>game-dev</code>.
119
+ </p>
120
+ <p>
121
+ <span className="docs-step-label">02</span>
122
+ <strong>Connect the local MCP server.</strong> See <a href="#/mcp">Connect via MCP</a> —
123
+ one JSON block, no account. Your agent can now find or import assets and share presets
124
+ with the labs through <code>.toonlab/</code>.
125
+ </p>
126
+ <p>
127
+ <span className="docs-step-label">03</span>
128
+ <strong>Author looks visually, apply them from code.</strong> Tune shaders and world
129
+ systems in the labs, export presets, and load them in your game — or fetch a published{' '}
130
+ <a href="https://toonlab.io/styles" target="_blank" rel="noreferrer">style bundle</a> with{' '}
131
+ <code>fetchStyleBundle</code>.
132
+ </p>
133
+ <p>
134
+ <span className="docs-step-label">04</span>
135
+ <strong>Prompt, run, iterate.</strong> Start from a goal prompt with a verifiable outcome
136
+ and let the agent check its own work — the <a href="#/prompts">prompt cookbook</a> has
137
+ ready-to-paste recipes.
138
+ </p>
139
+
140
+ <h2>Start here</h2>
141
+ <div className="docs-cards">
142
+ <a className="docs-card" href="#/library">
143
+ <BookOpenText size={18} aria-hidden />
144
+ <strong>Using the library</strong>
145
+ <span>Bootstrap a world, customize shaders, author assets, share your work.</span>
146
+ </a>
147
+ <a className="docs-card" href="#/mcp">
148
+ <Plug size={18} aria-hidden />
149
+ <strong>Connect via MCP</strong>
150
+ <span>The local server, its tool reference, and the optional Pro remote server.</span>
151
+ </a>
152
+ <a className="docs-card" href="#/prompts">
153
+ <Sparkles size={18} aria-hidden />
154
+ <strong>Prompt cookbook</strong>
155
+ <span>The first prompt to start a game, and recipes for the common jobs.</span>
156
+ </a>
157
+ <a className="docs-card" href="#/reference">
158
+ <TerminalSquare size={18} aria-hidden />
159
+ <strong>Settings reference</strong>
160
+ <span>Every tunable field with types, defaults, and ranges — generated from the schemas.</span>
161
+ </a>
162
+ </div>
163
+ </article>
164
+ );
165
+ }
166
+
167
+ /* ----------------------------------------------------------------- library */
168
+
169
+ const WORLD_SNIPPET = `import * as THREE from 'three';
170
+ import { createStylizedTerrain, createStylizedWorld } from '@call-me-sensei/toonlab';
171
+
172
+ // Seeded terrain — any seed is a valid, playable world.
173
+ const terrain = createStylizedTerrain({ seed: 42, size: 1000, archetype: 'terracedKarst' });
174
+ const terrainRoot = new THREE.Group();
175
+ terrainRoot.add(terrain.root);
176
+ scene.add(terrainRoot);
177
+
178
+ // Environment shading, sun + shadows, sky, water, LOD forests, grass,
179
+ // fog, and collision — all on by default.
180
+ const world = await createStylizedWorld({
181
+ renderer, scene, camera,
182
+ terrain: { heightAt: terrain.heightAt, root: terrainRoot, size: terrain.meshExtent },
183
+ water: { level: terrain.waterLevel },
184
+ weather: { preset: 'call_me_sensei' },
185
+ followTarget: character, // optional: splashes, wakes, grass push
186
+ });
187
+ character.position.copy(terrain.spawn);
188
+
189
+ const clock = new THREE.Clock();
190
+ renderer.setAnimationLoop(() => {
191
+ world.update(clock.getDelta());
192
+ renderer.render(scene, camera);
193
+ });`;
194
+
195
+ const TOON_SNIPPET = `import { applyToonShader, createToonSettings } from '@call-me-sensei/toonlab/toon';
196
+
197
+ // Every field ships a sensible default; override only what defines your look.
198
+ // 23 groups, 298 fields — see the Settings reference for all of them.
199
+ const settings = createToonSettings({
200
+ preset: 'default',
201
+ rimLight: { intensity: 0.35, mode: 'depth' },
202
+ outline: { thickness: 1.6 },
203
+ skinTone: { skinShadowBrightness: 0.94 },
204
+ });
205
+
206
+ applyToonShader(characterRoot, { settings });`;
207
+
208
+ const ASSET_SNIPPET = `import { createCatalog } from '@call-me-sensei/toonlab/catalog';
209
+ import { propAssetFromObject } from '@call-me-sensei/toonlab/propgen';
210
+
211
+ // Procedural catalog: every recipe/preset as a searchable manifest.
212
+ const catalog = createCatalog();
213
+ const lantern = catalog.spawn('prop/lantern/stone-toro', { seed: 7 }); // grounded, collided, LOD'd
214
+
215
+ // Any imported GLB (e.g. a CC0 model imported through the local MCP server)
216
+ // joins the same placement pipeline:
217
+ const shrine = propAssetFromObject(importedGltf.scene);`;
218
+
219
+ const TEXGEN_SNIPPET = `import {
220
+ createTextureSettings, evaluateTextureMaps,
221
+ findTexturePreset, syncTextureMapTextures,
222
+ } from '@call-me-sensei/toonlab/texgen';
223
+
224
+ // 25 tileable generators, layered overlays (moss, rust, grime), a
225
+ // cel-capable color ramp, and derived normal/AO/roughness/ORM maps —
226
+ // all CPU-baked from a 60+ preset library. No texture assets shipped.
227
+ const settings = createTextureSettings(findTexturePreset('mossy-bricks').settings);
228
+ const maps = await evaluateTextureMaps(settings, { size: 1024 });
229
+ const textures = syncTextureMapTextures(maps); // THREE.DataTexture per map
230
+ material.map = textures.albedo;
231
+ material.normalMap = textures.normal;`;
232
+
233
+ const BUNDLE_SNIPPET = `import { fetchStyleBundle } from '@call-me-sensei/toonlab/styles';
234
+
235
+ // One published document that styles many systems at once. Fetching public
236
+ // bundles needs no account; authoring them happens on toonlab.io (Pro).
237
+ const { settings } = await fetchStyleBundle('sakura-dusk'); // slug or full URL
238
+
239
+ applyToonShader(characterRoot, { settings: settings.toon });
240
+ sky.applySettings(settings.sky);
241
+ water.applySettings(settings.water);
242
+ // Other slots: environment, weather, grass, flowers, vegetationShader,
243
+ // tree, lighting, post — apply each through its system's runtime.`;
244
+
245
+ function Library() {
246
+ return (
247
+ <article>
248
+ <div className="docs-eyebrow">Runtime library</div>
249
+ <h1>Using the npm package</h1>
250
+ <p className="docs-lede">
251
+ <code>@call-me-sensei/toonlab</code> is a runtime library — your app owns the renderer and
252
+ the frame loop; ToonLab systems accept your objects. It is WebGPU-first (TSL /
253
+ NodeMaterial) with a WebGL2 fallback through the same path.
254
+ </p>
255
+
256
+ <CodeBlock label="Install" code={'npm install @call-me-sensei/toonlab'} />
257
+
258
+ <div className="docs-callout">
259
+ <Bot size={16} aria-hidden />
260
+ <span>
261
+ For every tunable field with types, defaults, and ranges, see the{' '}
262
+ <a href="#/reference">Settings reference</a> — generated from the same schemas that
263
+ drive the labs and the debug panel.
264
+ </span>
265
+ </div>
266
+
267
+ <h2>A complete open world</h2>
268
+ <p>
269
+ <code>createStylizedWorld</code> composes environment shading, an aligned sun with real
270
+ shadows, sky, water, LOD forests, follow-window grass, unified fog, weather, and collision
271
+ in one call. Terrain is a pure contract — <code>heightAt(x, z)</code> in meters plus a
272
+ displaced mesh — so you can use the seeded generator or bring your own.
273
+ </p>
274
+ <CodeBlock label="main.js" code={WORLD_SNIPPET} />
275
+ <p>
276
+ Archetypes: <code>terracedKarst</code>, <code>lakeland</code>, <code>alpine</code>,{' '}
277
+ <code>rollingPlains</code>, <code>archipelago</code>. Add solid props with{' '}
278
+ <code>world.collision.addCircles(...)</code> and a clickable minimap with{' '}
279
+ <code>createWorldMinimap</code>. The{' '}
280
+ <a href="/examples/outdoor-world/" target="_blank" rel="noreferrer">outdoor-world example</a>{' '}
281
+ is the reference project layout.
282
+ </p>
283
+
284
+ <h2>What each import gives you</h2>
285
+ <div className="docs-table-wrap">
286
+ <table className="docs-table">
287
+ <thead>
288
+ <tr>
289
+ <th>Import</th>
290
+ <th>What you get</th>
291
+ </tr>
292
+ </thead>
293
+ <tbody>
294
+ <tr><td>/toon</td><td>Anime character shader: cel bands, face lighting, rim, hair highlights, outlines — 23 preset-serializable settings groups.</td></tr>
295
+ <tr><td>/environment</td><td>Scene shader for texture packs and glTF: material-role classification, wrapped lighting, time-of-day, fog, cloud shadows.</td></tr>
296
+ <tr><td>/water</td><td>Gerstner-wave water with a calm→storm dial, breakers, foam, caustics, ripples, and a CPU spectrum mirror for buoyancy.</td></tr>
297
+ <tr><td>/sky</td><td>Gradient/sun/painterly-cloud/star system — 46 portable art fields per preset.</td></tr>
298
+ <tr><td>/weather</td><td>Cross-system coordinator: 22 presets, GPU precipitation, lightning; drives sky, water, wind, vegetation, and fog.</td></tr>
299
+ <tr><td>/vegetation</td><td>Instanced grass and flower fields, procedural trees, palettes, masks, and scatter helpers.</td></tr>
300
+ <tr><td>/lighting</td><td>Versioned light recipes, budgets, runtime realization, and a data-only UE5 handoff.</td></tr>
301
+ <tr><td>/post, /camera, /game-feel</td><td>Compositor pipeline, camera operator stack + director, and hit-stop/punch/flash game feel.</td></tr>
302
+ <tr><td>/texgen</td><td>Seamless CPU-baked PBR texture generator with 60+ presets and a natural-language recipe mapper.</td></tr>
303
+ <tr><td>/rockgen, /propgen, /buildinggen, /pathgen, /villagegen</td><td>Seeded generators for rocks, 12 prop families, building exteriors, road networks, and settlements.</td></tr>
304
+ <tr><td>/vfxgen, /ambientfx, /fauna</td><td>Gameplay VFX (trails, impacts, weapons), ambient particles (petals, fireflies), and GPU-animated creatures.</td></tr>
305
+ <tr><td>/catalog, /loaders, /debug</td><td>Searchable asset manifest with <code>catalog.spawn</code>, model loaders (GLB/VRM/PMX/FBX), and the schema-driven tuning panel.</td></tr>
306
+ </tbody>
307
+ </table>
308
+ </div>
309
+ <p>
310
+ The full cluster table with per-system deep dives is in the{' '}
311
+ <a href={`${GITHUB}#whats-inside`} target="_blank" rel="noreferrer">README</a> and{' '}
312
+ <a href={`${GITHUB}/tree/main/docs`} target="_blank" rel="noreferrer"><code>docs/</code></a>.
313
+ </p>
314
+
315
+ <h2>Customize a shader</h2>
316
+ <p>
317
+ Every ToonLab shader is driven by a settings object created from a schema — you never
318
+ write GLSL/TSL to get a custom look. The same schemas power the labs, so anything you can
319
+ click there is a field you can set in code.
320
+ </p>
321
+ <CodeBlock label="Custom character shader" code={TOON_SNIPPET} />
322
+ <p>
323
+ The intended workflow for a signature look: tune it visually in the{' '}
324
+ <a href="/shader-lab/" target="_blank" rel="noreferrer">Character Shader Lab</a>, export
325
+ the preset document, and load it in your game (<code>serializeToonPreset</code> /{' '}
326
+ <code>registerToonPreset</code>). For looks beyond the schema, the shaders are open-source
327
+ TSL modules — fork the cluster and keep the preset contract.
328
+ </p>
329
+
330
+ <h2>Create assets</h2>
331
+ <p>
332
+ Assets come from seeded procedural generators, the texture generator, and anything you
333
+ import — including CC0 assets your agent pulls in through the{' '}
334
+ <a href="#/mcp">local MCP server</a>. All of them end up in the same placement pipeline:
335
+ grounded, collided, instanced, with true-3D-distance LODs.
336
+ </p>
337
+ <CodeBlock label="Procedural + imported assets" code={ASSET_SNIPPET} />
338
+ <CodeBlock label="Procedural textures" code={TEXGEN_SNIPPET} />
339
+
340
+ <h2>Apply a style bundle</h2>
341
+ <CodeBlock label="Style bundle in the runtime" code={BUNDLE_SNIPPET} />
342
+ <p>
343
+ Bundles resolve through <code>GET https://toonlab.io/api/v1/bundles/:slug</code> — public,
344
+ no API key. Pass a full URL to use a self-hosted bundle document instead. Authoring and
345
+ publishing bundles happens in the{' '}
346
+ <a href="https://toonlab.io/styles" target="_blank" rel="noreferrer">bundle builder</a>{' '}
347
+ <ProBadge />.
348
+ </p>
349
+
350
+ <h2>Share your work</h2>
351
+ <p>
352
+ Locally, everything you author lives in portable JSON documents: labs save into the
353
+ disk-backed <code>.toonlab/</code> workspace (shared with the MCP server), and every lab
354
+ can export preset files you can commit to your repo or hand to anyone.
355
+ </p>
356
+ <p>
357
+ Publishing to the public <a href="https://toonlab.io/gallery" target="_blank" rel="noreferrer">gallery</a>{' '}
358
+ <ProBadge /> works through a toonlab.io account: save a creation to your cloud library,
359
+ set its visibility to public, and it gets a permanent page and appears in gallery search —
360
+ loadable by anyone, including by reference from style bundles.
361
+ </p>
362
+ </article>
363
+ );
364
+ }
365
+
366
+ /* --------------------------------------------------------------------- mcp */
367
+
368
+ const LOCAL_CONFIG = `{
369
+ "mcpServers": {
370
+ "toonlab-local": {
371
+ "command": "npx",
372
+ "args": ["-y", "@call-me-sensei/toonlab@latest", "--workspace", "/absolute/path/to/your-game/.toonlab"]
373
+ }
374
+ }
375
+ }`;
376
+
377
+ const LOCAL_TOOLS = [
378
+ ['get_workspace_info', 'Workspace path, migration status, and item counts.'],
379
+ ['search_assets', 'Search built-in procedural assets, saved library entries, lab presets, and files on disk.'],
380
+ ['get_asset', 'Complete asset, recipe, preset, library entry, or workspace file descriptor by id.'],
381
+ ['list_my_creations', 'List the disk-backed library, saved lab documents, presets, imports, and exports.'],
382
+ ['get_my_creation', 'Read a saved creation or small workspace file (binary as base64 with a direct path).'],
383
+ ['save_creation', 'Save a JSON or text creation into .toonlab/creations so labs and agents share it.'],
384
+ ['generate_asset', 'Generate a deterministic, editable asset recipe from a built-in catalog entry (seeded — no credits, no AI).'],
385
+ ['search_cc0_assets', 'Search CC0 models, textures, and HDRIs from Poly Haven and ambientCG.'],
386
+ ['get_cc0_asset', 'Source, attribution, and download metadata for one CC0 asset.'],
387
+ ['import_cc0_asset', 'Download a CC0 asset plus attribution manifest into .toonlab/imports.'],
388
+ ['get_generation_capabilities', 'Explains what generation the local server offers (procedural recipes only).'],
389
+ ['list_style_labs', 'List the style-domain recipe systems the workspace understands.'],
390
+ ['create_style_recipe', 'Author a style-domain recipe document (post, camera, game feel, …).'],
391
+ ['generate_style_presets', 'Generate seeded preset variations for a style domain.'],
392
+ ['validate_style_document', 'Validate a style recipe/preset document against its schema.'],
393
+ ];
394
+
395
+ function Mcp() {
396
+ return (
397
+ <article>
398
+ <div className="docs-eyebrow">Model Context Protocol</div>
399
+ <h1>Connect your AI coding agent</h1>
400
+ <p className="docs-lede">
401
+ The open-source package bundles a local MCP server: stdio transport, no account, no OAuth,
402
+ everything on disk. It shares the <code>.toonlab/</code> workspace with the browser labs,
403
+ so what your agent saves, the labs see — and vice versa.
404
+ </p>
405
+
406
+ <h2>Setup</h2>
407
+ <p>
408
+ With the labs running (<code>npm run dev</code>), open{' '}
409
+ <a href="/settings/" target="_blank" rel="noreferrer">/settings/</a> for a ready-made,
410
+ checkout-specific config to paste into your client. Or configure it directly from npm:
411
+ </p>
412
+ <CodeBlock label="MCP client config" code={LOCAL_CONFIG} />
413
+ <p>
414
+ The workspace directory is created on first use and is ignored by Git by default. See{' '}
415
+ <a href={`${GITHUB}/blob/main/docs/mcp.md`} target="_blank" rel="noreferrer">
416
+ Local MCP and workspace
417
+ </a>{' '}
418
+ for the full layout.
419
+ </p>
420
+
421
+ <h2>Tool reference (local server)</h2>
422
+ <div className="docs-table-wrap">
423
+ <table className="docs-table">
424
+ <thead>
425
+ <tr>
426
+ <th>Tool</th>
427
+ <th>What it does</th>
428
+ </tr>
429
+ </thead>
430
+ <tbody>
431
+ {LOCAL_TOOLS.map(([name, what]) => (
432
+ <tr key={name}>
433
+ <td>{name}</td>
434
+ <td>{what}</td>
435
+ </tr>
436
+ ))}
437
+ </tbody>
438
+ </table>
439
+ </div>
440
+ <p>
441
+ Note the boundary: the local <code>generate_asset</code> produces <strong>seeded
442
+ procedural recipes</strong> from the built-in catalog — deterministic and free. AI
443
+ generation (diffusion images, textures, 3D models) is a hosted Pro feature.
444
+ </p>
445
+
446
+ <h2>
447
+ The remote Pro server <ProBadge />
448
+ </h2>
449
+ <div className="docs-callout docs-callout--pro">
450
+ <Crown size={16} aria-hidden />
451
+ <span>
452
+ <a href="https://toonlab.io" target="_blank" rel="noreferrer">toonlab.io</a> hosts a
453
+ remote MCP server at <code>https://toonlab.io/mcp</code> (OAuth, Pro/Team plans) that
454
+ adds an indexed CC0 search with styled previews, AI generation on credits — concept art,
455
+ seamless textures, image→3D chaining — stored characters with reference-image
456
+ consistency, and your cloud library. It composes with the local server; run both. Setup
457
+ and its tool reference:{' '}
458
+ <a href="https://toonlab.io/docs/mcp" target="_blank" rel="noreferrer">
459
+ toonlab.io/docs/mcp
460
+ </a>.
461
+ </span>
462
+ </div>
463
+ </article>
464
+ );
465
+ }
466
+
467
+ /* ----------------------------------------------------------------- prompts */
468
+
469
+ function Prompts() {
470
+ return (
471
+ <article>
472
+ <div className="docs-eyebrow">Prompt cookbook</div>
473
+ <h1>Prompts that build games</h1>
474
+ <p className="docs-lede">
475
+ These prompts assume the recommended setup — the runtime installed, the{' '}
476
+ <a href={`${GITHUB}/tree/main/agents`} target="_blank" rel="noreferrer">ToonLab skills</a>{' '}
477
+ copied into your project, and the <a href="#/mcp">local MCP server</a> connected. Recipes
478
+ that need the hosted service are badged <span className="docs-pro">Pro</span>.
479
+ </p>
480
+
481
+ <div className="docs-callout">
482
+ <Lightbulb size={16} aria-hidden />
483
+ <span>
484
+ Pattern: <strong>goal → constraints → definition of done.</strong> Let the agent choose
485
+ APIs (that is what the skills are for), but always give it something observable to
486
+ verify — &quot;until I can walk to the shoreline&quot; beats &quot;set up a world&quot;.
487
+ </span>
488
+ </div>
489
+
490
+ <h2>Start a game</h2>
491
+ <PromptBlock
492
+ title="The first prompt"
493
+ prompt={`Using the ToonLab game-dev skill, set up a new Three.js + Vite project with
494
+ @call-me-sensei/toonlab. Build a 1 km seeded open world (archetype "lakeland")
495
+ with the bundled toon-shaded mannequin as the playable character, water, sky,
496
+ the "call_me_sensei" weather preset, post-processing, and a follow camera with
497
+ game feel. Follow the skill's assembly order and frame-loop contract, then run
498
+ the dev server and fix issues until I can walk from spawn to the shoreline
499
+ and swim.`}
500
+ note="The game-dev skill carries the assembly order and the frame-loop contract, so the agent wires update order and render ownership correctly on the first try."
501
+ />
502
+
503
+ <h2>Dress the world with assets</h2>
504
+ <PromptBlock
505
+ title="CC0 import via the local server"
506
+ prompt={`Use the toonlab-local MCP server to furnish the fishing village: search
507
+ CC0 sources for lanterns, crates, barrels, and a pier (search_cc0_assets),
508
+ import the best fits with import_cc0_asset, and place them with the propgen
509
+ placement pipeline so they are grounded, collided, and LOD'd. Show me what
510
+ came from where with attribution.`}
511
+ note="import_cc0_asset writes the files and an attribution manifest into .toonlab/imports for direct use by the project."
512
+ />
513
+ <PromptBlock
514
+ title="Procedural set dressing"
515
+ prompt={`Using the ToonLab outdoor-world and rockgen skills, scatter cliff rocks along
516
+ the north ridge and add a stepped stone path from the village to the shrine
517
+ with an arched plank bridge over the river. Keep everything walkable — the
518
+ path should flatten heightAt and register collision.`}
519
+ />
520
+ <PromptBlock
521
+ title="Seeded recipes from the catalog"
522
+ prompt={`With the toonlab-local MCP server, generate three lantern recipe variations
523
+ (generate_asset from the catalog entry, different seeds), save them as
524
+ creations, and spawn them along the pier so I can compare them in the game.`}
525
+ />
526
+
527
+ <h2>Textures and style</h2>
528
+ <PromptBlock
529
+ title="Procedural texture with texgen"
530
+ prompt={`Using the ToonLab texture skill, build a mossy stone texture for the shrine
531
+ path with texgen (start from the mossy-bricks preset), tune the ramp toward a
532
+ hand-painted five-stop cel look, and derive normal + AO maps.`}
533
+ />
534
+ <PromptBlock
535
+ title="AI-generated seamless texture"
536
+ pro
537
+ prompt={`Generate a 2k seamless mossy stone texture through the toonlab remote MCP
538
+ server (kind texture_image) and apply it to the shrine walls. Before
539
+ generating, call get_generation_capabilities and tell me the credit cost.`}
540
+ note="AI generation runs on toonlab.io (Pro plan + credits). The local texgen recipe above is the free procedural alternative."
541
+ />
542
+ <PromptBlock
543
+ title="Apply a style bundle"
544
+ prompt={`Fetch the public "sakura-dusk" style bundle (fetchStyleBundle in
545
+ @call-me-sensei/toonlab/styles) and apply every filled slot — toon shading,
546
+ sky, water, environment, post — so the whole game matches it. Wire it behind
547
+ a setStyle() function so I can swap bundles later.`}
548
+ />
549
+
550
+ <h2>Atmosphere and feel</h2>
551
+ <PromptBlock
552
+ title="Weather as drama"
553
+ prompt={`Using the ToonLab weather and lighting skills, add a day/night cycle and a
554
+ thunderstorm that rolls in at dusk: wind ramps in the grass and trees, rain
555
+ streaks and ripples the water, lightning drives the sky flash and the light
556
+ rig, and the storm clears to fireflies at night.`}
557
+ />
558
+ <PromptBlock
559
+ title="Anime combat feedback"
560
+ prompt={`Using the ToonLab game-feel and vfxgen skills, make the sword swing feel
561
+ anime: 60 ms hit-stop on contact, a camera punch, a trail ribbon that follows
562
+ the blade, impact sparks, and a one-frame white flash on the target. Keep it
563
+ all through the game-feel runtime so time scaling stays consistent.`}
564
+ />
565
+
566
+ <h2>
567
+ Characters <ProBadge />
568
+ </h2>
569
+ <PromptBlock
570
+ title="Consistent character art"
571
+ pro
572
+ prompt={`Using my stored ToonLab character Yuki (list_characters first to see her
573
+ options), generate a concept of her in the travel cloak, lantern in hand, on
574
+ the pier at dusk. Pass character: { ref: "Yuki", outfit: "travel cloak" } —
575
+ do not re-describe her appearance in the prompt.`}
576
+ note="Stored character profiles with reference-image consistency live on toonlab.io — the server composes appearance, art style, and references automatically."
577
+ />
578
+
579
+ <h2>Ship the loop</h2>
580
+ <PromptBlock
581
+ title="Iterate with verification"
582
+ prompt={`Play through the current build: spawn, walk to the village, buy the lantern,
583
+ sail past the reef in the storm. Fix anything that breaks the loop — falling
584
+ through terrain, unlit props at night, water clipping in the boat — and rerun
585
+ until the whole sequence works. List what you changed.`}
586
+ />
587
+
588
+ <p>
589
+ The extended cookbook — including the full Pro generation and character workflows — is at{' '}
590
+ <a href="https://toonlab.io/docs/prompts" target="_blank" rel="noreferrer">
591
+ toonlab.io/docs/prompts
592
+ </a>.
593
+ </p>
594
+ </article>
595
+ );
596
+ }
597
+
598
+ /* --------------------------------------------------------------- reference */
599
+
600
+ function slugify(text) {
601
+ return text
602
+ .toLowerCase()
603
+ .replace(/&[a-z]+;|&#\d+;/g, '')
604
+ .replace(/[^\w\s-]/g, '')
605
+ .trim()
606
+ .replace(/\s+/g, '-');
607
+ }
608
+
609
+ let referenceHtmlCache = null;
610
+
611
+ function referenceHtml() {
612
+ if (referenceHtmlCache) return referenceHtmlCache;
613
+ let html = marked.parse(settingsReferenceRaw, { async: false });
614
+ html = html.replace(
615
+ /<h([123])>([^<]+)<\/h\1>/g,
616
+ (_m, level, text) => `<h${level} id="${slugify(text)}">${text}</h${level}>`,
617
+ );
618
+ html = html.replace(/<h1[^>]*>[\s\S]*?<\/h1>/, '');
619
+ html = html.replace(
620
+ /href="([\w-]+\.md)"/g,
621
+ `href="${GITHUB}/blob/main/docs/$1" target="_blank" rel="noreferrer"`,
622
+ );
623
+ referenceHtmlCache = html;
624
+ return html;
625
+ }
626
+
627
+ function Reference() {
628
+ const html = useMemo(referenceHtml, []);
629
+ return (
630
+ <article>
631
+ <div className="docs-eyebrow">Generated reference</div>
632
+ <h1>Settings reference</h1>
633
+ <p className="docs-lede">
634
+ Every tunable field in <code>@call-me-sensei/toonlab</code>, generated straight from the
635
+ settings schemas that drive the labs and the debug panel — with type, default, range or
636
+ options, and whether the field is portable (saved in preset documents) or scene/runtime
637
+ state.
638
+ </p>
639
+ <div className="docs-callout">
640
+ <Bot size={16} aria-hidden />
641
+ <span>
642
+ <strong>Using a coding agent?</strong> Point it at the raw markdown source of this page:{' '}
643
+ <code>docs/settings-reference.md</code> in the repo (regenerate with{' '}
644
+ <code>node scripts/generate-settings-reference.mjs</code>), or fetch it from{' '}
645
+ <a href="https://toonlab.io/docs/reference.md" target="_blank" rel="noreferrer">
646
+ toonlab.io/docs/reference.md
647
+ </a>.
648
+ </span>
649
+ </div>
650
+ <div className="docs-md" dangerouslySetInnerHTML={{ __html: html }} />
651
+ </article>
652
+ );
653
+ }
654
+
655
+ /* --------------------------------------------------------------------- app */
656
+
657
+ const SECTIONS = [
658
+ { hash: '', label: 'Overview', component: Overview },
659
+ { hash: '#/library', label: 'Using the library', component: Library },
660
+ { hash: '#/mcp', label: 'Connect via MCP', component: Mcp },
661
+ { hash: '#/prompts', label: 'Prompt cookbook', component: Prompts },
662
+ { hash: '#/reference', label: 'Settings reference', component: Reference },
663
+ ];
664
+
665
+ function activeSection() {
666
+ const hash = window.location.hash;
667
+ return SECTIONS.find((section) => section.hash && hash.startsWith(section.hash)) ?? SECTIONS[0];
668
+ }
669
+
670
+ function App() {
671
+ const [section, setSection] = useState(activeSection);
672
+
673
+ useEffect(() => {
674
+ const onHashChange = () => {
675
+ const next = activeSection();
676
+ setSection((prev) => {
677
+ if (prev !== next) window.scrollTo(0, 0);
678
+ return next;
679
+ });
680
+ };
681
+ window.addEventListener('hashchange', onHashChange);
682
+ return () => window.removeEventListener('hashchange', onHashChange);
683
+ }, []);
684
+
685
+ const Body = section.component;
686
+
687
+ return (
688
+ <>
689
+ <header className="docs-header">
690
+ <a className="docs-brand" href="/">
691
+ <span className="docs-brand-mark">ト</span>
692
+ <span className="docs-brand-word">TOONLAB</span>
693
+ <span className="docs-brand-tag">Open Source</span>
694
+ </a>
695
+ <nav className="docs-topnav">
696
+ <a href="/">Labs</a>
697
+ <a href="/gallery/">Gallery</a>
698
+ <a href="/docs/" aria-current="page">Docs</a>
699
+ <a href="/settings/">Settings</a>
700
+ <a href={GITHUB} target="_blank" rel="noreferrer">GitHub</a>
701
+ </nav>
702
+ </header>
703
+ <div className="docs-frame">
704
+ <aside className="docs-side">
705
+ <div className="docs-side-label">Documentation</div>
706
+ <nav className="docs-nav">
707
+ {SECTIONS.map((item) => (
708
+ <a
709
+ key={item.label}
710
+ href={item.hash || '#'}
711
+ className={item === section ? 'active' : undefined}
712
+ onClick={(event) => {
713
+ if (!item.hash) {
714
+ event.preventDefault();
715
+ history.pushState(null, '', window.location.pathname);
716
+ setSection(SECTIONS[0]);
717
+ window.scrollTo(0, 0);
718
+ }
719
+ }}
720
+ >
721
+ {item.label}
722
+ </a>
723
+ ))}
724
+ </nav>
725
+ <div className="docs-side-foot">
726
+ <a href={GITHUB} target="_blank" rel="noreferrer">GitHub</a>
727
+ <a href="https://www.npmjs.com/package/@call-me-sensei/toonlab" target="_blank" rel="noreferrer">npm</a>
728
+ <a href="https://toonlab.io/docs" target="_blank" rel="noreferrer">Pro docs</a>
729
+ </div>
730
+ </aside>
731
+ <main className="docs-main">
732
+ <Body />
733
+ </main>
734
+ </div>
735
+ </>
736
+ );
737
+ }
738
+
739
+ createRoot(document.getElementById('app')).render(<App />);