BUILDING BLOCKS, NOT A BLACK BOX
Take it apart.
Try something different.
The sandbox uses ordinary HTML, CSS and JavaScript. There is no app framework, package install or server-side simulation to understand first.
01 / THE FILES
Start with the small pieces.
sandbox-core.js contains the three numerical models. They do not know about the page or canvas, so you can run their steps in a test. sandbox.js connects sliders and pointer events to those models and draws their state. sandbox.css handles the layout.
The ocean is kept separately under assets/vendor/web-ocean/ocean.js. Its original author is Maksymilian Dendura (Noniv); the source is used under the MIT licence. The upstream note identifies the exact source commit.
LOCAL EXPERIMENTS
Edit a file. Refresh the browser.
For the public sandbox alone, run python -m http.server 8080 from the repository, then open http://localhost:8080/sandbox.html. No build step is needed. Use /sandbox-guide.html for these notes locally; the extensionless routes on the live site are Apache rewrites. The account pages still require PHP and are separate from this static preview.
Start by changing a default in options in sandbox.js, or the rendering colours in drawWater. Changes to the underlying equations belong in sandbox-core.js. Reload to apply code changes; use the sliders for live parameter changes.
02 / THE LOOP
Keep the model separate from the drawing.
The browser provides animation frames at a rate that depends on the screen and available performance. The 2D models advance in fixed time steps instead of treating every display frame as the same amount of time. The renderer can then draw the current state without changing the physics.
on each display frame:
add elapsed real time to an accumulator
while enough time remains:
advance the model by one small fixed step
subtract that step from the accumulator
draw the current statePause stops model advancement. Step once advances exactly 1/60 second; the pendulum uses four smaller integration steps inside that interval. Reset restores the same initial conditions. Returning to a hidden tab does not fast-forward the model to catch up.
03 / TRY A CHANGE
Change one assumption at a time.
Ripple tank: disturb a cell, then change wave retention to compare how quickly the energy fades. The visible water is a height field with a shaded, refracted tile pattern; it does not simulate a full volume of liquid.
Double pendulum: adjust a mass or rod length, or nudge an angle. The acceleration uses the point-mass equations documented by myPhysicsLab. A fourth-order Runge–Kutta integrator advances the two angles and angular velocities. The code is small enough to follow from state to derivative to position.
Particle box: turn off gravity and set bounce retention to 100%. Try following one collision. Then lower the retention and watch the motion lose energy. The model uses discrete circle collisions, so it has limits at very high speeds or dense piles.
04 / THE OCEAN
Small files can still do serious GPU work.
The ocean follows the web-ocean implementation: wave spectra are evolved with GPU compute shaders and inverse FFTs, then used to displace and shade a water mesh. Sky rendering and post-processing produce the sunset scene. This is substantially more involved than the 2D wave grid.
It is loaded in a same-origin frame only after Launch ocean. Controls send named parameter updates to that frame. Leaving the scene or choosing Stop removes the frame; the adapted renderer destroys its GPU device when the page closes. Render dimensions are capped, and Balanced quality starts below full resolution. File size alone is not a measure of GPU memory or processing cost.
Our changes remove the upstream development logging and screenshot-upload requests, add pause and control messages, add touch camera input, and split the original bundle into a page shell and readable JavaScript. Its licence and copyright notice are retained. The rendering engine is credited to its original author; it was not written from scratch for this site.
05 / ADD YOUR OWN EXPERIMENT
One state, one step, one draw function.
A useful starting point is a spring: store its position and velocity, compute acceleration from displacement, then draw the result. Add one slider for stiffness before adding more features. Keep the model in the core file so its behaviour can be tested without a browser.
state: position, velocity parameter: stiffness acceleration = -stiffness * position velocity += acceleration * dt position += velocity * dt draw(position)
This simple integrator is an experiment, not a precision solver. Use smaller steps or a more suitable integration method as the system becomes stiffer. The existing tests in .github/tests/sandbox.cjs demonstrate finite-value, geometry, energy and collision checks.
Back to the sandbox ↗Read the models ↗Read the ocean renderer ↗