← SplineCONTENT HISTORY
WHAT CHANGED
Update to Spline
Snapshot Oct 24, 2026 · 13:29 UTC · version 1.2.0
Collection source: downloaded plugin package.
First saved snapshot
No earlier snapshot is available to establish a change.
Save Spline and check future changes in Following.
Up to 10 plugins free · no card required.
Compare saved observations
Download comparison JSONFull technical diff · 0 changed fields
Full snapshot data
{
"description": "Nature & organic scenes, stylized or believable: terrain and islands (displace layer vs customMesh), trees/rocks, water, clouds, graded skies, surface scatter, atmosphere, palettes, and ambient motion. Load for prompts like \"floating island\", \"forest\", \"landscape\", \"ocean\". (Planets/space have their own 'planets' skill.)",
"included_files": [],
"name": "spline-3d-organic-scenes",
"skill_md_contents": "---\nname: spline-3d-organic-scenes\ndescription: \"Nature & organic scenes, stylized or believable: terrain and islands (displace layer vs customMesh), trees/rocks, water, clouds, graded skies, surface scatter, atmosphere, palettes, and ambient motion. Load for prompts like \\\"floating island\\\", \\\"forest\\\", \\\"landscape\\\", \\\"ocean\\\". (Planets/space have their own 'planets' skill.)\"\n---\n\n# Organic / nature scenes\n\nRecipes for the scenes users actually ask for — islands, forests, landscapes,\noceans. The difference between \"3 shapes on a plane\" and a scene that\npasses design review is: layered silhouette, height-graded color, dense varied\ndressing, atmosphere, and out-of-sync motion. All of it is cheap with the\npatterns below. (Planet/space scenes are a different setting — load the\n`planets` skill for those.)\n\nREGISTER CHECK first (see # Style register): the finishes shown below —\nflat-shaded facets, cone pines, bright greens — are the STYLIZED register,\ncorrect when the user asked for low-poly/cute/toy or for an inherently\nfantastical diorama (a floating island wears it well). For an UNSTATED style\non a real place (\"a forest\", \"a mountain landscape\") keep the same structure\nbut swap the finishes: skip `flatShading`, keep default segment counts, use\nmuted natural tones, give the terrain a `layer('texture', { style: 'grassy\nground with dirt patches, seamless' })` + bumpMap (see materials-and-look),\nscatter denser and more varied, and prefer `object('tree')` / `object('rock')`\nlibrary imports for hero props — detailed models beat cone-pines when realism\nis implied.\n\n## Terrain — two paths\n\n**Displace layer (default).** A subdivided flat shape + a noise `displace`\nlayer gives rolling relief in three lines. Use it whenever the silhouette\ndoesn't need to be exact:\n```\nmesh('Plane', { width: 2400, height: 2400, widthSegments: 48, heightSegments: 48 });\nrotation({ x: -90 });\nposition({ y: 0 });\nlayer('displace', { displacementType: 'noise', intensity: 140, scale: 500, seed: 4 });\nshading({ flatShading: true });\ncolor('#5c9e57');\nrename('Terrain');\n```\n`intensity` = relief height, `scale` = feature size (bigger = broader hills).\nThe mesh MUST have segments to displace.\n\n**customMesh (when the silhouette matters).** Islands, cliffs, specific\nlandforms: compute a vertex grid (or concentric rings), displace `y` with\nsummed sines + `rand()`, fade the displacement to zero at the rim so edges\nstay clean. Build at REAL world size (vertices are not normalized). WINDING:\non the XZ plane an angle loop `(cos a, ·, sin a)` runs CLOCKWISE seen from\nabove, so up-facing triangles list the NEXT angle before the CURRENT one —\nfan `(center, s+1, s)`, band `(base+s, base+sn, next+s)` +\n`(base+sn, next+sn, next+s)` (see building-scenes for the full patterns; a\nslip is safety-netted by auto-flip/double-siding, but correct winding gives\ncleaner normals). Then `shading({ flatShading: true })` for the low-poly\nlook. Verify proportions with the `size` [w,h,d] reported by\n`run_code`/`get_scene` — don't guess from screenshots.\n\n## A floating island (the full pattern)\n\n1. TOP: a customMesh disc (rings of vertices, sine+rand hills, rim fading to\n the edge), grass green, flat-shaded.\n2. UNDERSIDE: a customMesh inverted cone/spike with jittered rings — craggy\n rock tapering to a point. Overlap its top ring INTO the top disc so the\n seam hides; a slightly darker `depth` layer sells the shadowed underside.\n IT SHOULD SHARE SOME VERTICES WITH TOP DISC so the seam is smooth, not a gap, \n make sure to use them in reverse though to avoid breaking winding order.\n4. DRESSING on top: trees + rocks + a pond (below). Scatter, don't hand-place.\n5. SATELLITES: a few small `Dodecahedron` rocks floating around the island at\n varied heights — each with its own slow bob/tumble loop.\n6. GROUP everything (`select(o => !o.isCamera)` first!), name it, give the\n group ONE slow bob; give clouds/satellites their own offset loops so motion\n is layered, not monolithic.\n\n## Trees, rocks, clouds (loop-scattered, always varied)\n\n```\n// pines: trunk + STACKED cone foliage — at least 3 tiers, each smaller and\n// overlapping the one below (a single cone reads as a flat party hat).\n// Cones default to soft corners (cornerRadius 8) — lower them so tiers\n// keep a crisp tip and edge. Compute each tree's offset ONCE and reuse\n// it so all parts line up.\nfor (let i = 0; i < 7; i++) {\n const s = 0.6 + rand() * 0.7;\n const dx = (rand() - 0.5) * 700;\n const dz = (rand() - 0.5) * 700;\n const green = rand() > 0.5 ? '#2e7d4f' : '#35905c';\n mesh('Cylinder', { radius: 7 * s, height: 60 * s });\n color('#6b4a2f');\n position({ y: 30 * s });\n translate({ x: dx, z: dz });\n for (let t = 0; t < 3; t++) {\n mesh('Cone', { radius: 45 * s * (1 - t * 0.25), height: 70 * s,\n radialSegments: 7, cornerRadiusTop: 2, cornerRadiusBottom: 2 });\n color(green);\n position({ y: (75 + t * 38) * s });\n translate({ x: dx, z: dz });\n }\n}\n```\n\n- Rocks: low-`detail` `Icosahedron`/`Dodecahedron`, random y-rotation + slight\n z-tilt, 2 gray tones, flat-shaded.\n- Clouds: 3–5 overlapping `Sphere` puffs per cloud sharing a FLAT BASE —\n biggest puff in the middle, smaller toward the ends. Keep default segments\n and SMOOTH shading: low-segment flat-shaded spheres read as boulders, and a\n loose cluster of random blobs never reads as a cloud. (Rounded `Cube`s\n still work for a toy look.) Place high, spread wide:\n```\nfor (let c = 0; c < 3; c++) {\n const cx = (rand() - 0.5) * 1800;\n const cy = 430 + rand() * 150;\n const cz = (rand() - 0.5) * 1400;\n const puffs = 3 + Math.floor(rand() * 3);\n for (let p = 0; p < puffs; p++) {\n const t = p / (puffs - 1); // 0..1 along the cloud\n const d = 60 + 90 * Math.sin(Math.PI * t) + rand() * 15; // big middle, small ends\n mesh('Sphere', { width: d, height: d * 0.8, depth: d });\n color('#ffffff');\n // bottoms all touch the plane y = cy → the classic flat cloud base\n position({ x: cx + (t - 0.5) * puffs * 55, y: cy + d * 0.4, z: cz });\n rename('Puff' + c);\n }\n select(o => o.name === 'Puff' + c);\n group();\n rename('Cloud');\n}\n```\n Drift the cloud GROUPS out of sync (phase-offset pattern in the animation\n skill) — animating puffs individually tears a cloud apart.\n- Alternative to loops — surface scatter. `select` returns an ARRAY of\n matches; `cloneTo` takes a single object, so index it:\n```\nconst terrain = select(o => o.name === 'Terrain')[0];\nselect(o => o.name === 'Tree');\ncloneTo(terrain, { count: 15, spreadType: 'random', seed: 3, hideBase: true });\n```\n `clone({ type:'grid', ..., randomness: true, randomnessObject: {...} })`\n jitters a patch instead (see building-scenes for the full randomnessObject\n shape).\n\n## Water\n\nA pond/lake: a flat `Ellipse` nestled slightly ABOVE the terrain low spot\n(avoid z-fighting), glassy:\n```\nmesh('Ellipse', { width: 260, height: 200 });\nrotation({ x: -90 });\nlayer('glass', { alpha: 1, roughness: 0.15 });\ncolor('#3a7bd5');\nopacity(0.75);\n```\nIts shimmer is MOTION, so it rides the scene's motion document (the\n`animation` skill) — drive the gradient angle per frame:\n`spline.updateObject(id, { material: { layers: [{ type: 'gradient',\nangle: 140 + Math.sin(t * 0.7) * 25 }] } })`.\nAn ocean: the displace-terrain recipe with low `intensity`, blue, with the\n`displace` layer's `movement` driven per frame the same way for swell.\n\n## Sky & atmosphere (never skip)\n\n- The REAL sky option first: `sky({ timeOfDay: 15, weather: 'partlyCloudy' })`\n — a physically-based atmosphere + volumetric clouds behind the scene, with\n a sun that follows `timeOfDay` (0-24; ~6 sunrise, 12 noon, ~18 sunset) and\n moods from `weather` 'clear' → 'storm'. Reach for it whenever the prompt\n names a time of day, weather, or an open outdoor sky. Its `lighting`\n (default on) lights the scene with the sun + a sky dome IN PLACE of the\n ambient fill — so skip the ambient tint below, keep at most one soft fill\n light, and dial `lightingIntensity`/`exposure` rather than stacking suns.\n- Stylized/diorama scenes can stay on the flat color instead:\n `background('#a8d4ff')` — pick a sky tone from the palette.\n- `fog({ useBackgroundColor: true, near: 900, far: 4500 })` — distant content\n melts into the sky. THE one-call depth upgrade (with `sky()` on, give fog\n an explicit horizon-ish `color` instead — there's no flat bg to match).\n- `environment({ ao: true, ambient: { color: '#bcd4ff', intensity: 0.7 } })`\n — grounding contact-darkening + cool sky fill (ambient matters when the\n sky's own lighting is off or the sky is off).\n- Key light: `light('Directional', { intensity: 0.9, color: '#fff3df',\n shadowRadius: 3 })`, moved up and to one side. Warm key + cool ambient is\n the classic outdoor mix. Keep it at or below 1 (up to 2 only if the live\n shot still reads flat after repositioning) — it stacks on the ambient\n fill, and a hotter key washes the daylight out.\n Terrain outgrows the default shadow footprint: a directional light's\n shadows only cover its `size` (default 2000 world units), and a\n landscape/mountain scene spans far more — give the key `size: 4000` or\n more, matched to the terrain's span, so shadows reach the whole area.\n Outdoors there is ONE sun: with `sky()` lighting on, the sky IS the sun —\n otherwise reuse an existing directional light\n (`select(o => o.isLight)` + `updateLight`) rather than adding a second,\n and don't scatter extra point lights for \"sunlight\".\n- For a graded horizon without the simulated sky, a big `Backdrop` behind the\n scene with a `gradient` layer (see materials-and-look).\n- No `fx` finishing pass: fog + AO + the key light ARE the finish. Post\n effects are opt-in — add bloom/vignette only when the user asks.\n\n## Palette\n\nPick 4–5 colors with VALUE contrast and repeat them: e.g. grass '#5c9e57' /\n'#35905c', dirt '#8a5a2b', rock '#75757c', sky '#a8d4ff', accent '#e0552b'.\nTwo greens beat one (alternate them per tree). `style([...])` recolors\neverything at the end if the mix drifted.\n\n## Motion (code-driven, layered, out of sync)\n\nScene life is CODE — one motion document drives everything per frame (see\n# Motion & interactivity; load the `animation` skill):\n\n- The hero group: ONE slow bob, `y = base + Math.sin(t * 0.4) * 8`.\n- Clouds/satellites: per-element frequency + phase so nothing syncs —\n see the phase-offset pattern in the animation skill.\n- Water: material layer writes (above). Cloner scatters stay STATIC —\n `randomnessObject.movement` is a noise phase offset, not motion; drive the\n objects themselves (or their group pivots) for drift.\n- Cameras stay OUT of animated groups, and the states machinery stays\n opt-in (`state-transitions` — explicit asks only).\n\n## Order of work\n\nTerrain → hero landform → dressing scatter → water → sky/atmosphere → lights →\nmotion → frame your check with `set_view({ objects: [...] })` → screenshot →\nfix the worst thing → screenshot → finish with\n`lookFrom({ azimuth: -35, elevation: 12 })` so the user lands on the hero\nangle (`lookFrom` moves THEIR viewport; `set_view` only aims your private\nscreenshot camera). Procedural scenes earn 3–4 screenshots; use them at\nmilestones, not per tweak.\n"
}SHA-256 of public snapshot: 916d9e7559ec2a4d355f6e0d74c02515212eb4194af3865bf736e0bbaab56705