{"id":46310,"plugin_id":"plugin_asdk_app_6acd235a70a48191812b3ba9c471b4ac","kind":"skill","collection_source":"plugin_package","comparison_source":null,"observed_at":"2026-10-24T13:29:11.560Z","digest":"ffe86fa51b289f9c93d2a0ade21d016c238e4738f9b517642a44f6d2e1cf0367","against":null,"payload":{"description":"Procedural terrain/ocean/tree/rock/globe/building generators, seeded surface scatter, the constraint-based layout() solver, and the miniworld/diorama, city-street, enclosed-terrain (canyon/cave) and creature-rig recipes. Load for prompts like \"forest\", \"island\", \"landscape\", \"ocean\", \"canyon\", \"cave\", \"city\", \"miniworld\", \"mini world\", \"diorama\", \"arrange the furniture\", \"place these neatly\", or any scene needing many natural assets or exact relative placement.","included_files":[],"name":"spline-3d-procedural-generation","skill_md_contents":"---\nname: spline-3d-procedural-generation\ndescription: \"Procedural terrain/ocean/tree/rock/globe/building generators, seeded surface scatter, the constraint-based layout() solver, and the miniworld/diorama, city-street, enclosed-terrain (canyon/cave) and creature-rig recipes. Load for prompts like \\\"forest\\\", \\\"island\\\", \\\"landscape\\\", \\\"ocean\\\", \\\"canyon\\\", \\\"cave\\\", \\\"city\\\", \\\"miniworld\\\", \\\"mini world\\\", \\\"diorama\\\", \\\"arrange the furniture\\\", \\\"place these neatly\\\", or any scene needing many natural assets or exact relative placement.\"\n---\n\n# Procedural generation & constraint layout\n\nPurpose-built generators beat hand-rolled customMesh loops: they are\ndeterministic (same `seed` => same mesh), tuned to look good, and cheap to\nvary. The `layout()` solver replaces coordinate guessing with declarative\nconstraints. Everything here composes with the rest of the DSL (color,\nshading, physics, code-driven motion...). And surface MARKS are not meshes at all:\na sticker/splash/impact/logo ON an existing surface is a `Decal` (see the\nSURFACE DETAIL rule in your core guidance), never a customMesh outline.\n\nPROCEDURAL AS RUNTIME BEHAVIOR: everything in this skill AUTHORS a static,\neditable result into the tree — one baked output of a generator. When the\nuser explicitly asks for procedural/generative CONTENT (\"procedural X\",\n\"generated at runtime\", \"different every time\"), they want the generator\nitself: build it in the HTML layer with `spline.createObject` /\n`spline.cloneObject` (load the `html-content` skill — raw vertex meshes\nincluded, via `createObject('CustomMesh', {vertices, indices?})`, and\ncustom TSL shader materials via `spline.createCustomMaterial` when the\ngenerated surfaces need looks the layer system can't express), and\nuse THIS skill to author what that script needs: template objects to clone,\nand any surface built with the tuned generators below — their\nterrain/ocean/tree algorithms are DSL-only, so at runtime you either\nhand-roll the vertex math or clone their authored output.\n\nThey are also TURN SAVERS — treat them that way. Build in a few big\nrun_code blocks, not many small ones. If one object looks wrong, edit or\nreplace THAT object; never delete and rebuild the whole scene.\n\n## Generators\n\n- `terrain({ width?, depth?, maxHeight?, resolution?, seed?, noiseScale?,\n  octaves?, falloff?: 'none'|'island', plateau?: 0..1, color?, name?,\n  position?: [x,y,z] })` — rolling low-poly heightfield, base plane at y=0 of\n  its position. `falloff: 'island'` fades height to zero at the rim (islands,\n  dioramas). Default ~2000 units wide. Name it if you'll scatter onto it.\n  `noiseScale` 1.5-3 = rolling hills (the default already is); higher packs\n  more, smaller features. DETAIL BUDGET: frequencies the grid can't\n  represent are clamped, and the grid comes from `resolution` — at the\n  default 96, cranking noiseScale/octaves stops adding detail almost\n  immediately. MOUNTAINS are not default hills: raise `resolution` FIRST\n  (200-240), then `noiseScale` 3.5-6, `octaves` 5-6, `maxHeight`\n  0.25-0.4 × width (the 0.14 default is a hill), `plateau` 0. A RANGE is\n  2-3 overlapping `falloff: 'island'` tiles at different seeds, widths,\n  and heights (taller behind) — one smooth low-res tile standing in for\n  \"mountains\" is the diorama-dough failure. Dress steep slopes with\n  scattered high-`angularity` rock(); above a tree line go snow\n  `'#e8ecf0'` or bare rock `'#8d8578'`, never green to the summits.\n  BIOME COLOR: the default is grass green — ALWAYS pass `color` for any\n  other biome (desert sand `'#d9c08b'`, snow `'#e8ecf0'`, bare rock\n  `'#8d8578'`, martian `'#b06b45'`). A green-tinted desert is a failed\n  desert; double-check the screenshot for leftover green. And in the\n  believable register even GRASS gets an explicit olive `'#5d7a4d'`-family\n  color — the bright default green reads as felt.\n- `ocean({ width?, depth?, waveHeight?, wavelength?, direction?,\n  choppiness?: 0..1, resolution?, seed?, color?, name?, position? })` —\n  open-water surface built from directional Gerstner swells (sharp crests,\n  wide troughs, horizontal chop), smooth-shaded. MANDATORY for every\n  ocean/sea/lake/water-body ask: NEVER fake water with a flat plane + a\n  displace layer or noise bumps (always reads as a flat sheet), and NEVER\n  hang the water's look on an AI texture. A flat-looking ocean is a\n  failed ocean.\n  SCALE: open sea wants `width` 6000-9000; `waveHeight` defaults to\n  width * 0.012 — pass up to width * 0.02 for heavy seas, down to\n  width * 0.004 only for calm harbor/lake water. Total relief comes out\n  ~2.5x waveHeight. `direction` aligns swells with the scene's wind.\n  MATERIAL: keep the generated deep teal or darken it, `lighting` low\n  (30-50) so the surface stays glossy-dark, add a subtle `noise` layer\n  (scale ~4-6% of width) as specular mottle so light breaks up across\n  the surface, a slightly lighter `background` +\n  `fog({ useBackgroundColor: true })` so the horizon melts. For a\n  \"simulation\"/moving-water ask, drive a `displace` layer's `movement`\n  slowly per frame from the motion document (`animation` skill) ON TOP of\n  the geometry — motion comes from code, the swell SHAPE comes from\n  ocean(); animated displacement alone is not an ocean.\n  FINAL CAPTURE for water: set the camera 18-35° ABOVE the surface looking\n  across the swells — a sea-level grazing angle plus fresnel washes the\n  whole frame to white/sky. The finished shot should be mostly WATER with\n  visible swell shapes, not mostly sky. SELF-CHECK the screenshot: if the\n  horizon is a straight line and the surface reads flat, the waves are too\n  small for this camera distance — double `waveHeight` (or move the camera\n  closer/lower) and re-screenshot before finishing.\n- `tree({ kind?: 'oak'|'pine'|'palm'|'birch'|'bush', height?, seed?,\n  leafiness?, style?: 'realistic'|'stylized', trunkColor?, leafColor?,\n  name?, position? })` — tree with trunk + canopy grouped, base at y=0.\n  Default height ~260. `style` DEFAULTS TO 'realistic' (lobed,\n  noise-displaced canopies clustered on visible limbs); pass\n  `style: 'stylized'` (smooth blob canopies) ONLY when the prompt asks\n  for cute/cartoon/toy scenes. In the believable register ALWAYS pass\n  explicit muted colors — the bright defaults lean stylized:\n  `leafColor` olive/forest ('#5d7a4d' broadleaf, '#3f5a44' conifer,\n  jittered per tree), `trunkColor` gray-brown ('#6b5744') — a mint or\n  lime canopy is a toy cue. tree() is a BAKED whole — right for scenery;\n  a tree that must GROW or move branch by branch is decomposed into\n  named per-branch parts instead — the `animation` skill's\n  structure-generation rules.\n- `rock({ size?, seed?, detail?: 0|1|2, angularity?: 0..1,\n  flattenBottom?: 0..1 (a NUMBER, not a boolean — 0.35 sits naturally),\n  color?, name?, position? })` — faceted boulder, base at y=0. Default\n  size ~120.\n- `globe({ radius?, style?: 'dots'|'relief', cellStep?, dotSize?,\n  oceanColor?, landColor?, name?, position? })` — an EARTH globe with\n  REAL continent geometry (built-in geographic mask — the viewer will\n  recognize the Americas, Africa, Eurasia, Australia). Use it for ANY\n  \"globe\" / \"earth\" / \"world with cities\" ask; NEVER build Earth from\n  noise blobs and never gamble on an AI texture for it. `style: 'dots'`\n  is the dotted data-viz look; `'relief'` is raised low-poly land. CITY\n  MARKERS: give the globe an explicit `position` (e.g. [0, 0, 0]); the\n  returned handle has `project(lat, long)` → [x,y,z] relative to the\n  globe's center — place a small emissive sphere at\n  `[gx + p[0], gy + p[1], gz + p[2]]` for each city's REAL coordinates\n  (New York 40.7,-74 · London 51.5,-0.1 · Tokyo 35.7,139.7 · Paris\n  48.9,2.4 · Sydney -33.9,151.2 · São Paulo -23.6,-46.6 · Cairo 30,31.2 ·\n  Mumbai 19.1,72.9 ...) — they will sit exactly on the right continents.\n  A slow code-driven yaw sells it — `rotation.y = (t * 8) % 360` on the\n  globe group in the motion document (`animation` skill).\n- `scatter({ on, source, count?, seed?, align?: 'normal'|'up',\n  spread?: 'random'|'even' (a distribution MODE, not an amount),\n  scaleJitter?: 0..1, rotationJitter?: degrees, hideBase?: boolean })` —\n  distributes `source` over\n  the surface of `on` with a seeded surface cloner + natural yaw/scale\n  jitter. `on`/`source` accept names or the handles returned by the\n  generators, and resolve when the block runs — so create + scatter works in\n  ONE run_code call. `hideBase` defaults true (the source becomes the\n  scatter template). Instances stay UPRIGHT by default (trees, props,\n  buildings keep the template's orientation, jittered only in yaw); pass\n  `align: 'normal'` to make instances hug the surface tilt instead —\n  right for rocks and pebbles, wrong for trees on steep ground.\n\nSeeds: vary `seed` per asset for variety (three pines with seeds 1/2/3 look\nlike siblings, not clones). Rerunning with the same seed reproduces the mesh\nexactly — useful for \"same tree but taller\".\n\nGROUNDING hero objects on terrain: the handle terrain() returns carries\n`heightAt(x, z)` — the surface height in the terrain's LOCAL frame (its\nbase plane is y=0 at wherever you positioned it). Give the terrain an\nexplicit `position` (e.g. [0, 0, 0]) when you'll stand things on it, then\nplace each object at `y = terrainY + heightAt(objX - terrainX, objZ -\nterrainZ)` (plus half the object's height if its origin is centered).\nFeet, paws, wheels and foundations must TOUCH the surface — a character\nhalf-sunk into the ground fails review. scatter() already does this\nautomatically for its instances.\n\n### Forest island in one block\n\n```\nconst ground = terrain({ width: 2600, maxHeight: 320, seed: 7,\n  falloff: 'island', color: '#6f9e58', name: 'Island' });\nconst pine = tree({ kind: 'pine', height: 300, seed: 3, name: 'Pine' });\nscatter({ on: ground, source: pine, count: 70, seed: 5 });\nconst boulder = rock({ size: 150, seed: 11, name: 'Boulder' });\nscatter({ on: ground, source: boulder, count: 14, seed: 9,\n  scaleJitter: 0.5 });\n```\n\nThen set atmosphere (fog, background, lighting) per the organic-scenes skill,\nand frame with lookFrom/orbit. For hero foreground trees, place 2-3\nindividually with `position` and distinct seeds instead of scattering them.\nKeep dark moods (night, storm) READABLE: strong key + rim on the subject,\nbackground a step lighter than the darkest surfaces, fog subtle — never let\nthe scene collapse to near-black.\n\n## Cities & streets — density is the point\n\n- `building({ width?, depth?, height?, style?: 'modern'|'futuristic'|\n  'blocky', seed?, litRatio?: 0..1, bodyColor?, windowColor?, litColor?,\n  name?, position? })` — a tower with seeded setback massing, roof\n  mechanicals/antenna and REAL per-window geometry (a dark-pane grid +\n  a seeded LIT subset — instant night-city window variety). Towers are\n  ALWAYS `building()` — never plain extruded boxes, and never glass\n  boxes waiting on an AI window texture: the texture service fails\n  often, and building()'s window grids + lit subset are GEOMETRY that\n  need no texture at all. One `building()` per tower, varied\n  `seed`/`height`/`style`/footprint, is the same effort and looks 10x\n  denser. Vary `bodyColor` VALUE between neighbors (readability rule).\n- STREETS make the city read as a city, not a bar chart. Between the\n  block rows lay dark asphalt strips (`mesh('Plane')` or thin boxes,\n  `#2a2d33`) at road width from the # Real-world scale table (a lane is\n  150-165; a two-lane street ≈ 300), then loop: center dashes (thin\n  white boxes ~5 × 120 with ~180 gaps), streetlight pairs (thin cylinder\n  + small emissive sphere, 250-400 tall) along the curbs, a few simple\n  two-box cars (~225 long, one per lane) with varied yaw and color, and\n  sidewalk trees (`tree({ height: 350-550 })` — street trees are\n  TALLER than streetlights; 120-tall blobs read as shrubs and\n  miniaturize the whole city) or planters. A crosswalk (4-6 white\n  strips) at intersections is 6 objects and sells the whole street.\n- LAYOUT: tower grid step = footprint + a REAL street corridor, not a\n  gap: e.g. 340 footprint + two-lane road 300 + sidewalks 2 × 80 ≈\n  800-900 between tower centers, jittered; tallest cluster in the\n  middle, lower blocks at the rim. (A tight ~420 step with slot-canyon\n  gaps is a stylized diorama cue — fine when that's asked for.) Fog + a\n  slightly lighter sky ground the far edge (READABILITY still applies\n  at night). For ground-level streetscapes (residential streets, towns,\n  storefronts) load the `architecture` skill — its streets &\n  neighborhoods recipe covers lots/setbacks, utility poles with sagging\n  wires, and street furniture at true scale.\n\n## Miniworlds & dioramas — the containment contract\n\nA \"miniworld\" / \"mini world\" / \"diorama\" is NOT a landscape on infinite\nground. It is a CONTAINED model on a floating base block with clear\nsquare/rectangular bounds — like a slice of world cut out with a knife:\n\n1. BASE SLAB first: a rectangular block with visible THICKNESS —\n   `mesh('Cube', { width: 1600, height: 140, depth: 1600, cornerRadius: 12 })`\n   in an earth/rock tone. The slab's side faces ARE part of the look\n   (layered cross-section: darker earth below, sand/grass rim at the top\n   edge). Stack a second, thinner, slightly smaller slab for the top\n   layer color when the biome needs it.\n2. EVERYTHING STAYS INSIDE the slab footprint. Nothing overhangs the\n   edge; trees/props near the rim sit fully on the slab. Scatter with a\n   `spread` small enough to keep instances off the edge.\n3. WATER is an INSET BLOCK, not a plane: a translucent box\n   (`opacity` ~0.55-0.7, glass or fresnel layer) sunk into the slab so\n   its cut face shows blue on the slab's side — with a thin `ocean()`\n   patch (small `waveHeight`, `width`/`depth` = the water area) laid on\n   top for the wavy surface. Shorelines are carved by the sand layer's\n   outline, not by falloff fading.\n4. GROUND detail: a thin `terrain()` (low `maxHeight`, `width`/`depth`\n   = slab size, no falloff) sitting ON the slab top gives dunes/hills\n   that still END at the square edge.\n5. BACKDROP: plain soft studio color (`background()`), NO fog-to-horizon\n   — the miniworld floats like a product shot. Soft key + gentle AO;\n   subtle contact shadow reads well.\n6. CAMERA: elevated 3/4 view (elevation ~30-40 degrees) with the WHOLE\n   slab in frame plus margin — the silhouette of the block is the\n   composition.\n7. SCALE: mini — dense small props, mini people/creatures; charm comes\n   from density inside the bounds, not from world size.\n\nThe same contract applies to any \"mini X\" (kitchen, city block, farm):\nbase slab, contained content, studio backdrop, whole-slab framing.\n\n## Enclosed terrain (canyon, cave, valley) + first-person scenes\n\nThe two classic failures here are a camera buried inside a wall and a\nnear-black interior. Both are avoided by BUILD ORDER:\n\n1. SHELL FIRST, one block: floor + walls + lights + rim overview camera.\n   Canyon floor: `terrain({ width: 6000, depth: 1800, maxHeight: ~120 })`\n   in bare-rock/martian tones. Walls: two rows of LARGE rocks\n   (`rock({ size: 400-700 })`, y-scale 2-3x, varied seeds) along BOTH\n   long edges — overlapping rocks read as stratified cliffs and always\n   leave an open corridor between them.\n2. LIGHT THE INTERIOR in the same block: default ambient does not reach\n   inside — add a warm key angled down the corridor plus 1-2 fill lights\n   INSIDE it, and keep the sky visible past the corridor ends so depth\n   reads. If a screenshot comes back near-black, raise the lights ONCE\n   (READABILITY rule) and move on.\n3. The overview camera looks down the corridor axis from ABOVE THE RIM\n   (elevation 25-35 degrees), whole corridor in frame — that is the\n   resting viewport AND the final capture. Never leave the viewport at\n   eye level inside the canyon.\n4. PROPS next (alien trees, gems, pools — scattered on the floor with\n   heightAt grounding), then the FIRST-PERSON RIG LAST: eye-height\n   (~170) play camera + controls in one block. If the run gets cut\n   short, the scene survives; only the rig is lost.\n5. HUD/score plumbing (counters, money labels): ONE turn maximum. If\n   variable binding isn't available, a static Text label is fine — say\n   so and move on.\n\n## Simple creature rigs (fish, birds, walkers)\n\nOne creature = ONE pivot. `add('Empty', { name: 'Fish1' })`, build the\nbody parts, select them and `group()` under that pivot, then DRIVE the\nGROUP from the motion document (`animation` skill — \"Orbits, spins &\ntravel\"): a travel path plus a small yaw sway computed from time, with\nper-creature phase offsets so the school feels alive. NEVER chain\nnested Empties as pivots for a simple swim/fly — one level is enough;\nnested-pivot rigs are how a run loses ten minutes to \"delete and rebuild\nthe rig\". If parenting comes out wrong, fix it with ONE group/reparent\ncall — never rebuild the creature from scratch.\n\n## layout() — constraint-based placement\n\n`layout({ constraints: [...], objects?, fixed?, region?, seed?,\niterations?, noOverlap? })` moves EXISTING objects so the constraints hold\n(simulated annealing — overlap avoidance is always on unless\n`noOverlap: false`). References are names/ids/handles. Positions update\nafter the block dispatches; call get_scene only when a later step needs the\nexact numbers — a confirmation pass after every layout() is a wasted turn.\n\nConstraint vocabulary:\n- `{ type: 'near', a, b, min?, max? }` — footprint gap between a and b within\n  [min, max] (units; default 0..60).\n- `{ type: 'apart', items: [...], min }` — every pair at least `min` apart.\n- `{ type: 'on', a, b }` — a rests on top of b (stacks y, keeps a inside b's\n  footprint). Lamp on desk, cup on table.\n- `{ type: 'against', a, b, margin? }` — a touches b's nearest side face\n  (sofa against wall).\n- `{ type: 'facing', a, b }` — rotates a so its front (+Z) aims at b.\n- `{ type: 'align', items, axis: 'x'|'z' }` — shared coordinate (chairs in a\n  line).\n- `{ type: 'inside', items? }` / `{ type: 'spread', items }` — keep in the\n  region / distribute evenly.\n- `fixed: [...]` — anchors the solver must not move (walls, the table).\n\n### Dining set\n\n```\nlayout({\n  constraints: [\n    { type: 'near', a: 'Chair', b: 'Table', max: 40 },\n    { type: 'near', a: 'Chair 2', b: 'Table', max: 40 },\n    { type: 'near', a: 'Chair 3', b: 'Table', max: 40 },\n    { type: 'near', a: 'Chair 4', b: 'Table', max: 40 },\n    { type: 'facing', a: 'Chair', b: 'Table' },\n    { type: 'facing', a: 'Chair 2', b: 'Table' },\n    { type: 'facing', a: 'Chair 3', b: 'Table' },\n    { type: 'facing', a: 'Chair 4', b: 'Table' },\n    { type: 'apart', items: ['Chair', 'Chair 2', 'Chair 3', 'Chair 4'],\n      min: 20 },\n    { type: 'on', a: 'Vase', b: 'Table' },\n  ],\n  fixed: ['Table'],\n  seed: 2,\n});\n```\n\nGuidance:\n- PRECISION-FIRST WORKFLOW: create objects with rough positions (or defaults),\n  then `layout()` them — never hand-compute touching/facing coordinates.\n- Prefer a SEPARATE run_code call for layout after creating objects: sizes of\n  objects created in the same block are estimated from geometry params, while\n  a second call measures true bounds. Same-block layout still works when the\n  stand-ins are simple boxes.\n- layout() solves in the objects' own coordinate frame — use it on TOP-LEVEL\n  objects (or siblings in one group), not across different groups.\n- If a constraint can't be satisfied (e.g. six sofas against one tiny wall),\n  the solver gets as close as it can; widen the region or relax min/max\n  rather than piling on weight.\n- Unresolvable names are skipped silently — double-check names with get_scene\n  if the result looks off.\n- TURN BUDGET: even a dense interior needs only a handful of rounds — shell +\n  furniture in 1-2 blocks, one `layout()` pass, decor, lighting, then ONE\n  camera framing (lookFrom/orbit) with at most one adjustment after a\n  screenshot. If time remains, spend it on materials and lighting — not on\n  re-checking, reframing loops, or rebuilding. Atmosphere (fog, haze,\n  background) is ONE pass after geometry and framing — if it reads wrong,\n  fix it in ONE adjustment; never spend multiple turns chasing haze.\n\nCross-references: organic-scenes (atmosphere, water, palettes for nature),\nbuilding-scenes (units, camera framing, dressing loops)."},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}