A technical deep dive into the math and procedural logic behind an infinite 3D flight path. Explore how trigonometry, seeded randomness, and dynamic evasion create a seamless,
high-performance creative coding experience.
Infinite Flight: The Math and Magic Behind My Portfolio
Creating a 3D portfolio that feels "alive" requires more than just placing models in a scene. It requires a blend of physics, deterministic randomness, and trigonometry to trick the eye
into seeing an organic, infinite world.
In this post, I’m breaking down the three core systems that power the flight path in my latest project.
The Physics of Flight (Trigonometry)
The "feel" of the flight is controlled by two primary trigonometric oscillations: Banking and Pitching. Instead of using complex physics engines, I use simple sine waves tied to the
user's scroll progress.
- A. Aerodynamic Banking
To simulate a plane turning, the camera tilts on its Z-axis. By using a $2\pi$ multiplier, I ensure that the plane completes exactly one "sway" cycle every 40 units of movement.
// From AboutRoom.jsx const chunkProgress = (scrollPosition % CHUNK_LENGTH) / CHUNK_LENGTH; // One full sine wave (360 degrees) per chunk const bankAngle = Math.sin(chunkProgress * Math.PI * 2) * 0.12; camera.rotation.z = baseRotation.z + bankAngle;
- B. Turbulence Pitching
To add a layer of "instability," I double the frequency of the pitch (the nose moving up and down). This simulates the effect of wind resistance or air pockets.
// Two full sine waves per chunk for a faster "wobble" const pitchAngle = Math.sin(chunkProgress * Math.PI * 4) * 0.05; camera.rotation.x = baseRotation.x + pitchAngle;
Building Infinite Worlds (Procedural Generation)
The portfolio uses an Infinite Chunk System. As you fly forward, chunks of the world are generated ahead of you and destroyed behind you. To keep the world consistent (so the same clouds
appear in the same places if you fly backward), I use a Seeded PRNG (Pseudo-Random Number Generator).
The Seeded Random Trick
Standard Math.random() is non-deterministic. Instead, I use a sine-based hash function seeded with the chunkIndex.
function seededRandom(seed) { let s = seed; return function () { // High-frequency sine hashing s = Math.sin(s * 9999) * 10000; return s - Math.floor(s); }; }
This allows the engine to rebuild the exact same "random" layout of clouds every time you visit a specific coordinate, using zero memory storage.
- The "Parting the Red Sea" Effect (Cloud Evasion)
One major challenge with a centered flight path is that procedural clouds might block the user's view of the content. To solve this, I implemented a Dynamic Evasion Logic.
As clouds approach the camera, they calculate their distance and "step aside" using a Smoothstep function.
// Calculate progress of the cloud approaching the camera const evasionFactor = (worldZ - evasionStart) / (evasionEnd - evasionStart); // Apply a smoothstep curve for natural movement const smoothedEvasion = evasionFactor * evasionFactor * (3 - 2 * evasionFactor); // Push cloud left or right to clear the center path const evasionX = smoothedEvasion * maxEvasion * (initialX >= 0 ? 1 : -1); mesh.position.x = initialX + evasionX;
This creates a "tunnel" through the clouds that feels intentional and cinematic, ensuring the UI text is always legible.
- Geometric Proceduralism (Sawtooth Walls)
In the main corridor, I don't use a single 3D model. The walls are generated mathematically. When a door is added to the layout, the system uses Math.atan2 to calculate the exact angle
needed to "fold" the wall into a sawtooth pattern.
// Calculating the hypotenuse and angle for the wall fold const angle = Math.atan2(outerX - innerX, doorZSpan); const segmentWidth = Math.sqrt(Math.pow(outerX - innerX, 2) + Math.pow(doorZSpan, 2));
1 // Calculating the hypotenuse and angle for the wall fold
2 const angle = Math.atan2(outerX - innerX, doorZSpan);
3 const segmentWidth = Math.sqrt(Math.pow(outerX - innerX, 2) + Math.pow(doorZSpan, 2));
This allows for a dynamic corridor that can change its shape, length, and door placement without ever needing to open a 3D modeling tool.
By combining these simple mathematical principles, I was able to create a high-performance, 60FPS experience that feels completely bespoke to every user.
Follow me on twitter for more awesome stuff like this @mumeyong
