← SplineCONTENT HISTORY

Update to Spline

Snapshot Oct 24, 2026 · 13:29 UTC · version 1.2.0

Collection source: downloaded plugin package.

WHAT CHANGED

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 JSON
Full 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