Three New Tutorials: Solar System, GLSL Shaders & localStorage

The tutorial section just grew from 8 to 11. A Three.js solar system for intermediates, raw GLSL fire-and-water shaders for the brave, and a beginner-friendly guide to saving simulation settings with localStorage. Here's the story behind each one.

Why Tutorials?

The simulations on this site are fun to watch, but the questions that come up most often are always the same: How do I build something like this? and Where do I start? Articles explain the theory behind each simulation, but tutorials give you a working project at the end. These three new ones fill gaps that kept coming up.

Tutorial #9 — Build a Solar System with Three.js

The idea

A solar system is one of the classic "first real project" ideas in 3D — you need orbits, hierarchical transforms (moons orbit planets that orbit the Sun), textures, and a camera rig. It touches almost every Three.js concept at once, which makes it a perfect intermediate tutorial.

What you build

Over six steps the tutorial walks through: creating the initial scene, camera, renderer and OrbitControls; building a glowing Sun with MeshBasicMaterial and a PointLight; adding textured planets with proper orbital radii; wiring up Kepler elliptical orbits using Math.cos and Math.sin with eccentricity; adding Saturn's ring with a RingGeometry and a transparent texture; and finally the animation loop with a fixed timestep.

// Elliptical orbit using Kepler's equations
const a = planetData.distance;           // semi-major axis
const e = planetData.eccentricity || 0;  // eccentricity
const angle = elapsed * planetData.speed;

planet.mesh.position.set(
  a * Math.cos(angle),
  0,
  a * (1 - e * e) / (1 + e * Math.cos(angle)) * Math.sin(angle)
);

The hardest part was explaining Kepler's laws without turning it into a physics lecture. The compromise: show the maths inline, keep the code short, and link to the full Solar System & Kepler article for anyone who wants the derivation.

Tutorial #10 — GLSL Fire & Water Shaders

The idea

This one is deliberately hard. Most simulation developers eventually need to write a custom shader, and the learning curve is brutal — there's no console.log, no debugger, no stack traces. This tutorial takes two common effects (fire and water) and shows how to build them purely in GLSL fragment code.

What you build

Six steps again: setting up a ShaderMaterial with vertex and fragment shaders; writing an fbm() fractal noise function from scratch; animating the fire gradient with a time uniform; building a scrolling water surface with layered noise and a Fresnel-like alpha rim; combining both into a split-screen demo; and performance profiling with renderer.info.

// Fractal Brownian Motion — the core of both effects
float fbm(vec2 p) {
  float sum = 0.0;
  float amp = 0.5;
  for (int i = 0; i < 5; i++) {
    sum += amp * noise(p);
    p   *= 2.0;
    amp *= 0.5;
  }
  return sum;
}

The critical teaching moment is that both fire and water use the same noise function — they just differ in colour mapping, scroll direction, and blending. Once you internalise that, procedural shaders click.

Tutorial #11 — Save Settings with localStorage

The idea

Every simulation on this site has sliders, toggles and presets — and every time you reload, they reset. This beginner tutorial shows how to fix that using three browser APIs at increasing levels of sophistication: localStorage, JSON, and IndexedDB.

What you build

The first three steps cover the common path: localStorage.setItem/getItem, serialising a full params object with JSON.stringify, and auto-restoring on DOMContentLoaded with a safe Object.assign merge so that new parameters added in code updates don't vanish.

Steps 4–6 cover the edge cases that bite in production: a _v version key with a migration function for when you change your schema, QuotaExceededError handling with a storageAvailable() feature detect, and a minimal IndexedDB wrapper for when you outgrow 5 MB.

// Safe restore with schema merging
function loadParams(defaults) {
  try {
    const raw = localStorage.getItem('sim-params');
    if (!raw) return { ...defaults };
    const saved = JSON.parse(raw);
    if (saved._v !== defaults._v) {
      saved = migrate(saved, defaults._v);
    }
    return Object.assign({}, defaults, saved);
  } catch {
    return { ...defaults };
  }
}

This is the shortest of the three at ~35 minutes — deliberately so. Persistence is a tool, not a project. Get in, learn the pattern, get back to building your simulation.

Related Simulations: Cross-Linking the Library

While writing these tutorials I also added "Related Simulations" blocks to 16 articles across the site. Each block sits above the article navigation and shows a grid of 4 linked simulation cards. The idea is simple: if you're reading about boids, you should be one click away from the flock simulation, the predator-prey model and the ant colony.

The blocks use inline styles with CSS custom properties so they inherit the site's dark/light theme automatically. No extra stylesheet, no build step, no JavaScript — just HTML and var(--color-*).

What I Learned

Writing tutorials is fundamentally different from writing articles. Articles explain how something works; tutorials explain how to make it work. That means every code block has to actually run. Every step has to produce visible output. And the order of concepts matters far more than in a reference article where you can jump around.

The biggest lesson: start with the result. Show the finished demo at the top, then reverse-engineer it step by step. When the reader can already see what they're building towards, every intermediate step feels motivated rather than arbitrary.

All three tutorials are live now. Start with the one that matches your level: