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

IdleMotion

IdleMotion moves what it holds while nothing happens to it, so a scene does not stand frozen. It has two kinds:

  • IdleMotionKind.Float, or "float": rises and falls by height metres, rolls by rollRadians and pitches by pitchRadians, as a boat at anchor does. Defaults: 0.04 m, 0.04 rad and 0.02 rad, over a periodSeconds of 5.
  • IdleMotionKind.Sway, or "sway": swings by angleRadians either way about axis, in its own axes, as a lantern on a hook does. Defaults: 0.07 rad about [1, 0, 0], over a periodSeconds of 3.

Each axis’s peak and the cycle’s length are named, and the whole pose repeats every periodSeconds. The float’s roll and pitch run ahead of its rise, so it never rocks on one beat. height is straight up in the world, whatever turns or scales the parent.

A float that rests within 2 m of a map’s sea, over ground the sea covers, rides the sea’s waves as well as its own motion. It rises by the swell at its rest point and tilts with the surface’s slope there, so a moored boat lifts and pitches as the waves pass under it. It reads the sea drawn in its own canvas, at the time that sea is drawn, so the hull meets the water it floats in. A boat that rides the waves wants a small height of its own, or none, since the waves already lift it. A float on a pole, a cliff or a pond keeps its own motion alone, and waves: false keeps it so over the water too. It rides the sea of the Landscape it stands in; outside one, it rides the canvas’s one sea, and none where the canvas draws two.

Its props are in IdleMotionProps. A value the map’s schema refuses, such as a periodSeconds of 0, throws and names the field.

IdleMotion moves its children’s drawing, and nothing else: no transform a system reads, no body, no collider and nothing a room streams. A room and a headless mount render its children unmoved. What it holds counts as moving, so the sun’s far shadow map draws it each frame rather than keeping it. Bob is the other way round: it moves the entity itself, its kinematic body included, on the room and every page. Use Bob for a thing a player stands on or bumps into, and IdleMotion for decoration.

An Npc whose base names a floating motion is drawn riding it: its feet follow the deck point under them as the motion rises and rolls, and it stays upright, turning only with the motion’s yaw. Its body, its transform and the room’s stream stay where they stand, as the motion’s do, so it stands on a still floor collider at the deck’s height. What it holds rides with it.

base names one of two things:

  • A map prop’s id, such as "boat", for a prop with an idleMotion. A base the Landscape’s map lacks, or a prop with no idleMotion, throws as the NPC mounts and names the props that float.
  • A handle defineIdleMotion declares at module scope, for a float a game draws itself. defineIdleMotion takes the same options as IdleMotion, and <IdleMotion motion={handle}> draws it. While no IdleMotion that draws the handle is mounted, the NPC is drawn where it stands: unmount the NPC with the boat it rides, as one hidden at sea still floats. Two IdleMotions that draw one handle in one canvas throw in development.

Unreal’s walking character follows its movement base in position and yaw, Godot’s CharacterBody3D follows a platform’s position alone, and Unity’s parenting tilts it with the platform. Each of them moves the body; base moves the drawing, because an idle motion moves no body.

pivot is the point it turns about, [x, y, z] in its own metres before its scale. It defaults to the origin, which is the base of a model stood on its lowest point. Give a boat its waterline and a hung lantern its hook.

seed sets where in its cycle it starts, and two copies with one seed move in step. Left out, the seed comes from where it stands when it first draws, so a row of lanterns never swings together.

Every IdleMotion on a canvas reads one timeline, on the clock the map’s water and grass run on. The devtools’ pause and a pausing modal hold it, and it runs at the wall clock’s pace whatever the devtools’ speed. A model that loads late joins the timeline where it stands rather than starting at its own zero. A player’s reduced-motion setting, or the device’s, holds every IdleMotion at rest.

Each IdleMotion writes one matrix a frame on the page. It adds no draw call of its own, but what it holds draws apart from any batch, so a row of copies drawn one by one costs a draw call per mesh each. An InstancedModel takes the same motion as idleMotion, and moves every copy in one draw.

A map’s model or shape prop takes the same options as idleMotion, so a boat floats with no code.

Unity and Godot animate a prop’s idle motion with an animation clip or a tween, Roblox with TweenService, and Unreal with a Timeline or a material’s vertex motion. drei’s Float wraps what it moves, as IdleMotion does, with a speed and two intensities and a random start. IdleMotion names each axis’s peak and the cycle’s seconds instead, and starts from a seed, so a capture repeats and a row stays out of step.

The engine’s tests mount this file:

packages/engine/test/outside/HarbourIdle.tsx
import { IdleMotion, IdleMotionKind } from "@spawnite/engine";
// A raft riding the swell and a sign swinging on its bracket. Only what
// is drawn moves: an entity, a body or a collider round them stays put.
export function HarbourIdle() {
return (
<>
<IdleMotion
kind={IdleMotionKind.Float}
periodSeconds={4.5}
height={0.05}
rollRadians={0.05}
pivot={[0, 0.2, 0]}
>
<mesh name="raft" position={[0, 0.1, 0]}>
<boxGeometry args={[2, 0.2, 3]} />
<meshStandardMaterial color="#8a5a35" />
</mesh>
</IdleMotion>
<group position={[4, 2.5, 0]}>
<IdleMotion kind="sway" angleRadians={0.08}>
<mesh name="sign" position={[0, -0.4, 0]}>
<boxGeometry args={[1, 0.6, 0.05]} />
<meshStandardMaterial color="#d9b26b" />
</mesh>
</IdleMotion>
</group>
</>
);
}