SphereCluster
A hundred and one lit spheres that fly in, settle, breathe, and open where you point.
Options
-
background#fbfaff -
css colour
Behind the cluster. The near-white the hero sits on in the page this came from; the spheres are a pale blush and need a light ground to be pale against.
-
body#c7a5a5 -
css colour
The spheres' own colour, before the light and the glow reach them. A dusty rose, which with a pure red glow over it is what gives the cluster its colour from every angle.
-
glow#7a1414 -
css colour
The glow every sphere carries regardless of the light. This is the emissive, and it is doing more for the colour than the body is: an emissive is not lit, so it survives into the shadow side where the diffuse has gone, which is why the cluster reads warm all over rather than warm on one side. The scene this came from sets it to pure red. On r128's colour pipeline that renders as a blush; on a current one it saturates the red channel completely, which stops the bright side clipping to white and leaves every sphere a flat coral. A darker red puts the highlights back.
-
light#ffffff -
css colour
The key light, which does the modelling.
-
keyStrength0.52 -
0 to 3, good: 0.3 to 1
How bright the key is. Half the flat light, which is the balance the scene was lit at: the flat light is most of what you see and the key only has to say where the form is.
-
ambient#ffffff -
css colour
The flat light that reaches every surface whichever way it faces.
-
ambientStrength1 -
0 to 3, good: 0.6 to 1.4
How bright the flat light is. At full strength, as the scene has it, which is why the cluster is evenly lit and the shapes are carried by the silhouette rather than by shading.
-
distance24 -
8 to 60, good: 18 to 34
How far the camera sits from the cluster, in scene units. It is pulled back further on a narrow frame so the cluster still fits, which matters on a phone: at that aspect it otherwise runs off both sides.
-
intro2.6 -
0 to 8, good: 2 to 4
Seconds the entrance takes for one sphere, before the stagger. At 0 the cluster starts settled, which is what a backdrop behind a page that is already open wants.
-
stagger0.02 -
0 to 0.2, good: 0.01 to 0.04
Seconds between one sphere setting off and the next. The whole entrance therefore lasts the intro plus a hundred of these, so at the default it is finished a little after four and a half seconds.
-
drop25 -
2 to 60
How far below the frame the spheres start, in scene units. Far enough that none of them is in shot before it sets off.
-
overshoot4 -
0 to 12
How far past its resting place each sphere flies before falling back, and how far it bulges towards the camera on the way. The bulge is a half sine over each stage, so it returns to the resting depth exactly.
-
breathe0.1 -
0 to 1, good: 0.05 to 0.25
How far each sphere drifts once settled: a sine in height against a cosine in depth at half the amplitude. At 0 the settled cluster is still.
-
breatheSpeed2 -
0.1 to 8, good: 1 to 3
How fast it breathes, in radians a second. Each sphere is a fifth of a radian behind the one before it, which is what makes the cluster shimmer rather than pulse as one body.
-
part2.2 -
0 to 4
The size of the shove a hovered sphere is given, along the normal at the point the cursor ray met its surface, which is a point on the side facing you. It is delivered over about a second rather than at once, and the collision pass carries it outward into the neighbours, so sweeping the cursor pushes the whole cluster about and it drifts home afterwards.
-
partRadius4.5 -
0.5 to 10
How far along the cursor ray a sphere can be and still be hovered, as a multiple of itself. It exists only to stop a ray that has left the cluster entirely from catching something behind it.
What it needs
- Category
- Backdrops, an effect
- Dependencies
- three@>=0.160.0
- Browser
- WEBGL2
- Frame budget
- ~1.2 ms · 1 context
- Pinned to
- v0.13.0
- One InstancedMesh rather than a mesh each: the same geometry at the same tessellation, one draw call and one matrix per sphere instead of a hundred and one draw calls.
- frustumCulled is off. An InstancedMesh computes its bounding sphere once and keeps it, and the first frame of the entrance has every sphere below the frame, so the cached bounds sit off screen and every later frame is culled against them. That records as a take of empty background with no error anywhere.
- Which sphere the cursor is over is a real ray-sphere intersection against the displaced centres, not the nearest centre on the ground plane. The plane version picks the wrong sphere wherever the cluster is deep under the cursor, which is most of it: the nearest in x and y can be well behind the one whose surface you can see.
- The entrance is a closed form in t rather than a timeline, so it can be seeked. The disturbance cannot be, and carries state; it is identically zero until the pointer touches it, which is why a take is still a pure function of t.
- A hundred and one spheres at sixty-four segments square is about eight hundred thousand triangles, affordable exactly once because every instance shares the one geometry.
- The collision pass is every pair, five thousand distance tests, and it runs only while something is actually out of place. It is what spreads the disturbance rather than what prevents overlaps, so it cannot be dropped: without it the interaction is one sphere twitching. Measured with pnpm gputime at 0.21ms a frame and about 1ms on a frame where the cluster is being pushed.
Concept credit: The whole scene: the hundred and one hand-placed sphere positions and radii, the dusty rose body under a red emissive, the three light intensities, the twenty-five degree camera, and the staggered three-stage entrance. The arrangement is placed rather than generated and that is most of why it reads as composed, so it is carried across as his rather than replaced with a hash. What could not come across is the physics: its pointer force decays frame to frame and its collision pass tests every pair, and both are accumulators that would stop renderAtTime being seekable., by Ray Velez. The implementation here is written from scratch.