
Every Launch Week is an opportunity for Supabase to experiment, try some spicy new designs, and dogfood our own technology. During our previous Launch Week we took Generative AI for a spin. This time we decided to shoot for the stars.
For Launch Week 8, we wanted to make the user-generated tickets a central piece of the launch week theme. To do this, we built a “constellation” of stars - an animated night sky where every user signup was represented as a star, in the form of an “8” shape.
We could approach this animation in a few ways.
For example, animating the stroke-dashoffset
on an SVG path, similar to this example, was a good option, but it would have been difficult to randomize and change the shape at a later stage. Other approaches included 2D animation libraries, like Framer Motion , gsap or PixiJS .
Ultimately we decided to take Three.js for a spin using React Three Fiber (R3F) giving us a more powerful toolset to enable us to achieve the best possible result.
Learning Three.js is not a walk in the park but R3F abstracted many of its complexities such as cameras and renderers, to name a few. If you're new to R3F, some of the core primitives they provide for a basic scene include:
Geometries
: used to create and define shapesMaterials
: manage the texture and color of objectsMesh
: used to instantiate polygonal objects by combining a Geometry with a MaterialLights
: to shine bright like a diamond 💎🎵Canvas
: where you define your R3F Scene
If you want to dive a little deeper, here are a few good resources we found to get a solid grasp on the topic:
- I wish I knew this before using React Three Fiber - from our very own Greg
- Three.js Journey - by Bruno Simon
Setting up the scene
In this article, we’re going to break down the steps to reproduce the Launch Week 8 animation using React Three Fiber in NextJs.
These are the dependencies we’ll need:
If you’re using React 17, we’ll spare you the trouble of finding the last compatible R3F version:
All we need for each particle is a circle geometry with a minimal amount of sides to minimize complexity.
A basic standard material with a white color will do just fine. Using the AdditiveBlending module from three
provides a more interesting touch when particles happen to overlap, making them shine brighter:
Let’s put it together in an R3F Canvas
element and wrap up the initial setup with an ambientLight
, which will make objects visible, just as real light does:
For more context, the dpr
values help with pixelation issues and the camera
[0, 0, 500] position means that the camera is moved 500 units back in the z-axis to actually see the center [0,0,0] of the scene.
One thing to note is that the R3F Canvas renders a transparent background, so in order to see the white particle, we need to set the background of the parent html element to a dark color.
We created a separate component for the Particle, which will later encapsulate the animation logic.
Load users from Supabase
You might have noticed we haven’t instantiated the particles yet. As we mentioned earlier, we wanted each particle to represent a ticket generated by a user and stored in the database. Let’s fetch the signups from the tickets
table in our Supabase project (you might need to start your own Launch Week to fill your table):
We updated the constellation in realtime whenever a new ticket was generated, but we’ll skip over this part to keep the article shorter. Since it’s all open-source, you can dive deeper here if you wish.
Animating the particles
To move the particle around the screen we are going to leverage a few different concepts: useFrame and trigonometry 🤯
useFrame
Generally, the most optimal way to animate things in a browser viewport, using javascript, is by leveraging a method called requestAnimationFrame , which “tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation right before the next repaint.”. R3F has a similar hook called useFrame that lets you execute code on every frame of Fiber's render loop. We’ll use this to change the position of the particles over time in a few moments.
Using time as an animation variable
We can extract time information from the useFrame clock
parameter, to know how much time has elapsed in our application, and use that time to animate a value. Updating the x
position with Math.sin() generates a horizontal oscillating movement. Multiply it with a widthRadius
variable to customize the amplitude of the movement.
Combine the previous horizontal movement with a Math.cos()
on the y
position to draw a circle:
Calculating the circumference we can get the time the x position takes to complete a full circle.
When that happens, we can invert the cos sign on every other loop to obtain a basic 8 shape.
At this point, we played around with a number of parameters that made the animation more randomized and interesting.
For example, we randomized the speed and the delay of each particle:
We offset the shape on the x-axis, to concentrate most of the particles in the core of the 8 shape and leave a smaller amount externally, by playing around with exponentials using Math.pow() in combination with some more randomization.
Honestly, this was the result of a lot of playing and tweaking around, and we certainly didn’t hit the best possible result on the first try. Perhaps you want to take some time to experiment with the math - you might find even better and more configurable results.
GUI playground
What really helped to visualize the shape, gather feedback, and decide on a final design was adding a GUI to play around with the values. You can try for yourself by appending #debug
to the supabase.com/launch-week#debug url. Go crazy with it.
We used the dat.gui library:
Which needs to be loaded asynchronously, otherwise it raises a window is not defined
error.
Then we prepared a useParticlesConfig
hook with all the configuration wired up to the GUI. Whenever the GUI updated, we also updated react state.
Here is the final code:
Now THAT’S how you create a new constellation ✨. Feel free to use the code and learnings to build your own.
Conclusion
In this journey, you saw how to use Three.js and harness the power of React Three Fiber and creative experimentation to craft an animation. We leveraged trigonometry, animation hooks, and GUI playgrounds to build a "8" shape formed by user-generated stars.
If you loved this and the new Launch Week 8 branding, make sure to tune in on Monday at 09 AM PT as we unveil the full landing 💥