In circles and spheres

Although not every effect or demo in the wilderness is circular, it's common to see a lot of them using circular functions in one way or another.

Math.sin and Math.cos are handy because they loop, like a wave that goes up and down. When the given value goes over *Math.PI 2, the value they give you it's going to repeat. Both sin and cos are actually both the same function but offset by HALF_PI.

Their circular nature makes it super useful for repeating things. Want a repeating scale animation? Slap a cosine to it.

let scale = Math.cos(time)

Want something to go in circles? Slap both of them with the same angle.

position.x = Math.sin(time);

position.y = Math.cos(time);

Getting started

Daydream sphere

In daydream, Keita creates 3 spheres. In their fragment shader, he samples simplex-noise using the normals of the spheres. Then, discards any pixel where the noise is lower than 0.3 . This create the gaps that allow us to see the most inner spheres

float n = snoise(vec3(normal.x + time , normal.y, normal.z));

float visibility = step(0.3, n);

if (visibility < 1.0) discard;

Daydream sphere by Keita

Stripes around orb

This animation is similar to the daydream sphere. However, the effect is limited to a single stripe, and it has a lot more spheres. Here's a basic version of what it would look like in the fragment shader:

float stripeSize = 0.2;

float stripe = step(0.5 -stripeSize, uv.y) * step(uv.y, 0.5 + stripeSize);

float n = snoise(vec3(normal.x + time , normal.y, normal.z)) * stripe;

Using the sphere UVs I calculate a white-centered stripe. Then, multiply that by the noise to get the noise only happening in the center of the sphere.

tsoong.d sphere animation

Rectangle around circle

To position a lot of rectangles like in mrDoob's demo, we first divide 2PI by the number of rectangles:

let anglePerSquare = (Math.PI * 2.) / count

Then we loop over all our meshes and calculate their x and y position:

// in some loop
let angle = index * anglePerSquare
mesh.position.x = Math.cos(angle) * radius
mesh.position.y = Math.sin(angle) * radius


Demo by mrdoob

Circle particle torus

While this looks way more complicated than the last demo. A torus is a set of circles around another circle. It's not even an extra step from the last demo, It's the same step repeated twice!

// 1s loop

let pivot = new Object3d()
let torusAngle= index * anglePerPivot
pivot.position.x = Math.cos(torusAngle) * torusRadius
pivot.position.y = Math.sin(torusAngle) * torusRadius

Then, for every pivot (an inner loop), we repeat the process above but add each mesh to the pivot.

Looper 169 by Spite

Dave circular motion

This kind of circular motion also worsk with cosine and sine. However, they are applied to the scale and y positoning of the rings:

// loop over all rings.

let offset = ringIndex * Math.PI + time * Math.PI

let minScale = 0.2;

let maxScale = 1.;

ring.scale.setScalar( (Math.sin(offset) * 0.5 + 0.5) * (maxScale - minScale) + minScale )

ring.position.z = Math.cos(offset) *0.8

For the circular motion to work correctly, you need to use both cos and sin.

:) by dave

Extra resources.