Idea
I have been thinking for a while that the home page of this site looks a bit like a warning for an invalid page in it’s current state…
Current Home Page
In order to redesign this page, I started thinking of some ideas to make it a bit prettier, while keeping with the origami theme. After some iterations of designs including butterflies that would follow the mouse and hidden text only visible if a light drifted nearest (in the end, both of these might have been supremely annoying for a user), I settled on the idea of making an origami boat with fireflies floating about it.
Implementation
Onshape
To begin to implement this, I looked for origami models that you could import into javascript code. There’s a bunch of online repositories (Sketchfab, Turbosquid – which both sound like nerdy superhero names) with models in different formats like GLTF that are compatible with javascript. I found some pretty crazy models, including a “steampunk underwater explorer” and a detailed model of a white rhino. There was one beautiful model of origami flowers here which was close to what I was looking for.
Since I hadn’t found exactly the model to use, I turned to CAD. If ski skills were measured in the same way to CAD competency, then my level would be solidly on the bunny slopes, probably a kid being pulled on a waist leash. I used Onshape online to build the origami model of the boat. After folding a paper boat to use as a model, I noticed you could make a very cheatsy model by extruding a diamond to point from bottom to top, and overlap extruding a diamond to small diamond top to bottom, and then removing the bottom shell. Unfortunatly, CAD, like jokes, can suffer from explination, so here’s a picture.
Onshape Try 1
Luckily, this is a simple enough model that you only need a few tools to build it up in it’s truer form. The origami boat is symmetrical about two lines and made from a bunch of triangles on different planes.
Onshape Try 2
Three.js
Once the model was exported as a GLTF, it was quite nice to import it into a Three.js script using the GLTFLoader library. I set up the script with the usual suspects – camera, renderer, and orbit controls. The scene was set.
Shaders
The boat looked quite plain with nothing around it, and so I decided to add in some ripples, to make it look a bit like it was just floating gently in a pond (or lake or whatever body of water you like).
To get the ripple effect, I experimented with Shaders. Shaders are very common in graphics programming and basically are small programs that describe how something is rendered from the geometry to the texture. From my understanding, in Three.js, there’s two types of shaders: the vertexShader and the fragmentShader. You can go into Three.js and access the shader code (written in GLSL) and modify it. For creating the ripples, I primarily modified the fragmentShader, which describes each pixel.
First, I created a simple circle. There are multiple passes of shaders – first the vertexShader then the fragmentShader. You can pass the position of each vertex to the fragment shader (which interpolates since it’s at a pixel level) and the vUv – which is the 2D representation of a coordinate with texture information (on the 2D texture map). This concept is definitely still fuzzy for me, so I’d be curious if there are good resources for learning about this. Anyway, here was my general process to create a ripple pattern:
- Create circle (use distance function from center to defined radius)
- Create ring (inner and outer radius)
- Create moving ring (use uniform float time to change the radius)
- Add blur to ring (use smoothstep function)
- Fade out ripple (use variable alpha and fade out factor to define opacity for color)
- Add multiple ripples (for loop to start ripples at random times from 0-2 seconds)
Here is the finalized code:
const fragmentShader = `
uniform float time;
uniform float rippleStartTimes[${maxRipples}];
varying vec2 vUv;
void main() {
vec3 color = vec3(0.0, 0.5, 1.0);
float alpha = 0.0;
for (int i = 0; i < ${maxRipples}; i++) {
if (rippleStartTimes[i] > 0.0) {
float rippleAge = time - rippleStartTimes[i];
float distanceFromCenter = distance(vUv, vec2(0.5, 0.5));
float radius = rippleAge * 0.02;
float edgeThickness = 0.005;
float innerEdge = radius - edgeThickness / 2.0;
float outerEdge = radius + edgeThickness / 2.0;
float ringGradient = smoothstep(innerEdge, outerEdge, distanceFromCenter) - smoothstep(outerEdge, outerEdge + edgeThickness, distanceFromCenter);
float fadeOut = 1.0 - rippleAge * 0.2;
alpha = max(alpha, ringGradient * fadeOut);
}
}
gl_FragColor = vec4(color, alpha);
}
`;
A nice way to play around with shaders is with Shadertoy.
“Fireflies”
I added the shader code and now the boat sort of looked like it had ripples beneath it. Yet, there was still something missing – insect like looking lights (the solution to some, if not many problems). I was inspired by this example on the Three.js page which had little point lights swirling around an lighting up a slightly ominous model of a man’s head. To move the lights around, I first started with a simple orbit, centered around the model’s position on the scene. This looked extremely mechanical for insects, and didn’t provide the interesting lighting I was hoping for. Into the orbits, I added a bit of randomness in height variation, and also scaled down the movement of the lights a lot so that it didn’t look like frantic flies frenzying about a poor boat. Here was the movement equation applied to teh lights at each time step:
const amplitude = 0.75;
light.position.x = origamiBoat.position.x + amplitude * Math.sin(baseFrequency * time + index * Math.PI / 2);
light.position.z = origamiBoat.position.z + amplitude * Math.cos(baseFrequency * time + index * Math.PI / 2);
// Slightly adjust the Y position for a gentle floating effect
light.position.y = origamiBoat.position.y + 0.5 + 0.05 * Math.sin(baseFrequency * time);
Result
MP4
GIF
Here’s a gif from a screencast of the scene.
Gif
GitHub Project
Here’s the code for the project. Install packages and run npx vite
to see on local machine.
Homepage
Instead of overlaying text and making this the new homepage, I decided to just leave this project as it was. While I’d still like to update the homepage a bit, I do like the idea of this website being very simple – primarily composed of markdown files and html pages – very static content. It was fun to use some new tools like GLSL and Onshape to create a small little scene. I’m hoping to explore more concepts in computer graphics in the future.