Jesse’s Ramen: 3D Portfolio Case Study
How I made my 3D interactive portfolio for the web
It’s been about a year and a half since I released my portfolio in the style of a 3D interactive ramen shop. Lots has happened since then — I’ve shifted gears from management consulting to full-time software engineering, started freelancing, and even kicked off teaching a course (but more on that later).
I know I promised way back to spill the beans on how I built this my portfolio. Well, better late than never, right? So, grab your favourite bowl of ramen, and let’s dive into the nitty-gritty of how I made this happen.
1. The Inspiration
Let me present to you the amazing work of Brandon James Greer. He’s a pixel artist and he has a youtube channel where he makes videos about his work. You can find it here.
If it’s not already clear, the idea for my ramen shop draws heavily from his pixel art concept of a futuristic, cyberpunk style sushi stand. The vision of a cyberpunk ramen shop, inspired by his pixel art, struck me precisely when I was brainstorming a unique portfolio website to display my skills, interests, and projects.
The moment I laid eyes on that artwork, my mind exploded with ideas. I saw an opportunity to harness the chaotic vibrancy of a cyberpunk setting as a way to encapsulate and highlight everything that represents me and my passions.
There was just one missing piece.
2. The Technology
So I had all these ideas buzzing around in my head. I knew I wanted to create something interactive, something that would not just showcase my work but also engage users in a unique way. But how was I going to build it? That’s where three.js came into the picture. It’s a powerful JavaScript library and API used to create and display animated 3D computer graphics in a web browser. Perfect for what I had in mind.
After spending a few months learning three.js as well as following a few beginner blender tutorials to help me build out my vision in 3D, I was able to translate the chaotic, neon-lit aesthetic of the cyberpunk world into a dynamic, interactive experience. The best part? I could make it completely web-based, allowing anyone, anywhere, to visit my ramen shop portfolio without any special software.
For those wondering, this website was designed entirely in vanilla three.js. Would it be easier for me to build it in React Three Fiber now that I’ve spent a year and a half developing with React and Next.js? Possibly. But here’s my take: Vanilla three.js provides a fundamental understanding of 3D web graphics, revealing the core mechanics essential for problem-solving and optimization. The skills acquired are broadly applicable, even to other game engines, offering versatility without the layers of abstraction found in more complex frameworks. While I often use React Three Fiber for client projects, for beginners, I recommend starting with vanilla three.js to build a strong, transferable foundation that’s beneficial in any coding path.
3. The Look
What immediately captures visitors’ attention upon seeing the site is its distinct aesthetic. The design features a lively and colourful night scene, brimming with stylistic elements. Soft shadows add depth and realism, while subtle reflections enhance the overall visual appeal, completing the vibrant cyberpunk ambiance. Let’s break down how I achieved this in three.js.
Modelling
I’m not a good 3D modeller. In fact, this ramen shop may have been one of the first few 3D models I created. Luckily the style I was going for didn’t really require any complex modelling and there’s loads of videos online that will break down the process of modelling step by step for beginners.
For a lot of the shop, I took inspiration from this video from Ru’s Blender Diary:
Baking
Let’s face it, a lot of three.js sites out there have a certain retro, PS2-era 3D look to them. Getting polished and clean 3D graphics on the web is no small feat. It’s even more challenging to achieve high-quality visuals with appealing colours and soft shadows. Modern gaming consoles boast cutting-edge rendering techniques and powerful hardware, far beyond what a typical browser, often running on a mobile device, can handle.
That’s why ‘baking’ becomes our secret ingredient. Think of baking in 3D graphics as pre-cooking certain scene elements. In my case, I set up all the lighting in Blender, which can churn out far superior images compared to three.js, thanks to the heavy lifting done by my computer’s powerful hardware for ray tracing.
Notice the grainy parts in the blender viewport? That’s the tell-tale sign of the intense power required for baking. Even the most potent desktop GPUs break a sweat trying to produce raytraced scenes at 60fps. But here’s the trick: what if you handled all this heavy computation in advance and saved the output as an image? Then, when someone visits your website, you simply take this ‘pre-cooked’ image and wrap it around the model. The result is a scene that looks like it was rendered with high-end equipment, but in reality, it’s just cleverly using pre-rendered images to simulate that effect.
Here’s an example of one of those baked images. This one was used for texturing all the machines on top of the building. Notice how all the lighting a shadow information is already “baked” into the texture.
Bloom
The next visual element likely to catch your eye is the use of bloom, responsible for all the captivating, glowy neon lights in the scene.
Bloom, in the realm of three.js and computer graphics, is a postprocessing effect. This means it’s applied after the initial rendering of the scene, enhancing the final image without altering the underlying 3D models or textures.
When I created this website, bloom was all or nothing. To selectively render objects with bloom, the scene had to be rendered twice. The first pass involved rendering only the bloomed objects with the bloom effect activated, while all other objects were turned black to prevent them from blooming. The second pass rendered the scene again, but this time with the bloom effect turned off, capturing everything else. Finally, these two images were combined, overlaying the bloomed elements onto the rest of the scene to achieve the desired visual effect.
Here’s an official three.js example showcasing how this is achieved.
I wouldn’t recommend this approach anymore, however, as the community eventually discovered how to achieve this effect without having to do so much extra work. This new technique is also a lot more performant as you don’t have to go about rendering your whole scene twice:
Reflections
In my opinion, implementing reflections significantly boosted the visual appeal of the site, and surprisingly, it wasn’t too difficult to achieve. Three.js comes with a handy Reflector class in their addons package. I used this as a starting point, tweaking the code slightly to reduce the opacity for a subtler effect.
The method involves creating a secondary camera, positioned to capture the scene from a reverse angle, effectively creating a mirrored image. This technique, while straightforward, essentially renders the scene twice, which can impact performance. To mitigate this, I rendered the reflections at a lower resolution and adjusted the opacity. This approach ensured the reflections were less conspicuous and didn’t noticeably degrade the site’s performance
If you look real close, you can definitely tell that the reflections are a lot more pixelated than the regular scene.
Hologram
The holographic ramen bowl perched atop my virtual ramen shop is another effect that significantly enhances the site’s ambiance, yet it was surprisingly simple to implement. Once again, I took inspiration from an official three.js example — there’s a lot of really good stuff there:
This example transforms the vertices of a 3D model into points in three.js. My process involved crafting a 3D model of a ramen bowl in Blender, placing three.js points in all the areas where the vertices live, and then applying a constant rotation. The result was an eye-catching, holographic ramen bowl that effortlessly adds to the site’s cyberpunk flair.
4. The Functionality
I’ve received numerous questions over the past year and a half about the functionality of the website. Looking back, I would tackle these functionalities differently today, considering the less scalable aspects of my original approach. However, the way I initially set up the site was quite straightforward. Let’s delve into that.
Camera
The smooth camera transitions throughout the site are achieved using hardcoded GSAP animations, which temporarily disable the orbitControls during transitions. By simultaneously animating the camera’s position and the target property of orbitControls, the camera fluidly moves to any designated part of the scene. It’s important to adjust the orbitControl properties, like the minimum and maximum angles, to align with the new camera positions and angles, ensuring seamless navigation.
this.transitions.default = async (duration) =>
{
this.controls.enableRotate = false
this.controls.enableZoom = false
gsap.to(this.instance.position, { duration: duration, ease: "power1.inOut",
x: -11.1,
y: -1,
z: -7.6})
gsap.to(this.controls.target, { duration: duration, ease: "power1.inOut",
x: 0,
y: 0,
z: -1})
await this.sleep(1500)
this.controls.enableRotate = true
this.controls.enableZoom = true
}
Screens
Many have been curious about how I implemented the ‘onScreen’ navigation — from choosing projects via the vending machine to exploring my background on the big TV screen. The solution is surprisingly straightforward: they’re all just images! Instead of using complex iFrames or embedded HTML, I created the graphics in Photoshop and managed the navigation through a logic controller.
And the interactive part? I placed 3D hitboxes over the navigation areas and used a raycaster to detect clicks. For instance, clicking on the hitbox where the ‘back’ button is located takes the user to the previously viewed image.
In this illustration, I’ve used red rectangles to represent the hitbox locations. The actual hitboxes in the project are 3D blocks and, of course, they’re invisible.
Performance
The site is loaded with fancy visuals and interactive features, and since most users are likely to visit it on their phones, I had to ensure a smooth experience for all, including those with less powerful devices. The 3D model and lighting are relatively light for most phones due to the use of ‘baked’ lighting instead of real-time lighting. However, there are a few elements that significantly impact performance: reflections, video textures, and bloom.
To address this, I implemented a system that gauges the device’s performance when loading the site. Based on this, it selectively disables features to maintain an acceptable framerate (around 45fps). This involves disabling reflections, pausing videos, turning off bloom, and switching to matcaps for lighting to mimic the outer glow effect. I firmly believe that no matter how sophisticated the experience is, it must run smoothly; otherwise, it risks losing its appeal.
Conclusion
Alright, that’s the behind-the-scenes tour of Jesse’s Ramen shop. I hope it has demystified the process and inspired you to explore the possibilities of three.js.
I understand that diving into 3D web development can seem daunting at first glance. All of these different techniques and tricks put together might appear overwhelming. But that’s all it is! Just a bunch of examples that I found online and a little bit of creative engineering and I was able to go from complete beginner to creating a super fun and interactive website in less than 6 months (while working full time).
In fact, I’m teaching a beginner friendly three.js to allow students to go from zero three.js knowledge to building this site as the final project:
Check it out here if you’re interested:
Zero to Mastery is doing its first ever Black Friday, Cyber Monday sale so if you use the discount code BFCM23 before November 28th, you can get 27% off the annual membership and you’ll automatically be entered to win a brand new Macbook Pro (~$2,000 value).
The key takeaway from my journey is that while the end results may look complex, the path to learning and creating with three.js is filled with opportunities for creativity and innovation, and it’s more accessible than you might think. So, whether you’re a seasoned developer or a curious newcomer, I encourage you to dive in, experiment, and see where this powerful tool can take you. Who knows? Maybe your portfolio will be the next to amaze and inspire the web development community.