Skip to content
Work in progress. These docs describe Spawnite at launch, and some parts are still being built.

Water material

<WaterMaterial> is the material of the mesh it sits in, inside a World. It draws the mesh as water: a lake, a river or a sea. Two layers of noise drift across each other as waves. The water turns lighter in the shallows near its edge and breaks into foam at the shore. Where the view grazes the surface, it catches the sky. The waves run on the effects’ clock, as a map’s sea and its grass do: the devtools’ pause and a pausing modal hold them, and the devtools’ speed leaves them at the wall clock’s pace, so a pond keeps the sea’s pace at 2x. A devtools step does not move them. A water you write on Shader runs on the world’s clock instead.

Its props are in WaterMaterialProps.

The shore is the edge of the plane’s texture coordinates: the square, or the circle inside it when round is on. Draw the water on a planeGeometry, because another mesh’s edge does not follow that square, and part of its rim would have no foam. Lay the plane flat where the water should be, a little above the ground, and size it to the lake or the river. The waves keep the same size on any plane, because they follow the ground rather than the plane’s corners.

The water draws in one pass, without a depth texture or a second render of the scene, so a phone can afford it. Its sky is a fixed colour, not a true reflection: trees and the sun do not mirror in it.

Roblox makes water a terrain material, with wave size and speed set on the terrain. Unity’s HD pipeline ships a water system, and Godot leaves water to community shaders. Three’s own Water draws the scene a second time for its reflection, which costs a phone a frame’s worth of work. Here water is a material with props, as the CRT material is, and a map’s sea lays the same water across the whole map, its shallows and foam where the ground under each point rises to the surface rather than at the plane’s edge. Its waves run on the effects’ clock rather than the world’s, as Godot’s shader TIME runs unscaled while Unity’s _Time follows timeScale: water is the map’s ambient motion, so it keeps the grass’s pace whatever the devtools’ speed.

The shader’s source is waterFragment. It is an exported GLSL string with a comment per block: copy it, change a block, such as the sky colour or the foam, and pass it to a Shader for water of your own.

A water plane laid on a map’s ground draws over the ground, and the map’s trees, grass, tufts, bushes and rocks keep growing through it. Clear the ground under the pond with two brushes of the map editor: the MCP’s edit_terrain, spawnite map terrain, or the devtools’ Terrain section. Give each the pond’s centre and a radius a metre past its edge, since a brush weakens toward its rim, and a strength of 1:

  1. scatter with multiplier: 0 over the families tree, tuft, bush and rock. The tuft multiplier thins the grass blades as well as the tufts. A tree that stands just past the radius can still hang its canopy over the water: widen the brush for tree alone, or erase that one tree. erase is also for the single instances left standing once the multiplier is 0.
  2. paint with a material that grows no grass, such as sand, for a shore.

map_query at the pond then reads its grass as bare, and both edits live in the map’s file, src/maps/<map>.json.

A round pond in the meadow:

import { Entity, WaterMaterial, World } from "@spawnite/engine";
export function Pond() {
return (
<World map="meadow">
<Entity position={[0, 0.05, -4]}>
<mesh rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[6, 6]} />
<WaterMaterial round waveSize={1.5} />
</mesh>
</Entity>
</World>
);
}

Client. The water changes nothing in the world; each player’s browser draws its own. A player walks over it as over the ground: it has no collider, and no swimming.