
The Project
The goal of this project was to create a website where visitors can explore some of the major projects I've worked on. Beyond serving as a web portfolio, I aimed to make the website itself a noteworthy project, showcasing my frontend development capabilities through various innovative elements.
My Role
I developed this project entirely by hand, coding every part of the website in HTML, CSS, and JavaScript without using any website creation services.
3D Element
The most interesting part of this website is on the landing page, where an interactive 3D model of myself appears behind the main title. This model was created using a Gaussian splatting scan, a process that involves taking multiple photos and generating a model with software like Polycam.
To embed the model into the website, I used the JavaScript library three.js. This required several steps:
- Creating a scene to place the model.
- Setting up a camera to view the model within the scene.
- Ensuring the renderer for the scene adjusted to the size of the container holding it.
import * as THREE from "three";
import { GLTFLoader } from "three/addons/loaders/GLTFLoader.js";
var myContainer = document.getElementById("container3D");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
let object;
let mouseX = window.innerWidth / 2;
let mouseY = window.innerHeight / 2;
const loader = new GLTFLoader();
loader.load(
"self_portrait.glb",
function (gltf) {
object = gltf.scene;
scene.add(object);
object.position.y = -0.8;
object.position.x = -0.43;
},
undefined,
function (error) {
console.error(error);
}
);
const renderer = new THREE.WebGLRenderer({
alpha: true,
antialias: true,
});
renderer.setSize(myContainer.clientWidth * 1.5, myContainer.clientHeight);
document.getElementById("container3D").appendChild(renderer.domElement);
camera.position.z = 1;
const light = new THREE.AmbientLight(0xffffff);
scene.add(light);What makes this element particularly engaging is that it follows mouse movements. This was achieved by tracking mouse movement in the script and rotating the model accordingly, using events and functions.
function animate() {
requestAnimationFrame(animate);
object.rotation.y = -2.9 + (mouseX / window.innerWidth) * 3;
if ((mouseY * 0.5) / window.innerHeight >= 1.4) {
object.rotation.x = 1.4;
} else {
object.rotation.x = (mouseY * 0.5) / window.innerHeight;
}
renderer.render(scene, camera);
}
document.onmousemove = (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
};Animated Features
One prominent feature on both the main page and the portfolio gallery page is the flip animation on the project cards. This was implemented using primarily CSS and HTML. The cards use preserve-3d to create a true 3D flip effect:
.project {
display: flex;
height: 25rem;
width: 100%;
position: relative;
transition: 0.9s;
transform-style: preserve-3d;
}
.flip {
transform: rotateY(180deg);
}
.front,
.back {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
backface-visibility: hidden;
transition: 0.9s;
transform-style: preserve-3d;
}In JavaScript, an event listener on the button adds the flip class to the card when clicked. The code is streamlined so that instead of each card needing its own event listener, the function identifies the button number and matches it to the corresponding card.
document.addEventListener("DOMContentLoaded", function () {
const buttons = document.querySelectorAll(".card-btn");
buttons.forEach((button) => {
button.addEventListener("click", function () {
const projectNumber = this.getAttribute("data-btn");
const project = document.querySelector(
`.project[data-project="${projectNumber}"]`
);
project.classList.toggle("flip");
});
});
});Final Product
Everything here is the final product — you're looking at it.
Progress Updates
Mar 26, 2026
Complete Rebuild with Next.js
Rebuilt the entire portfolio from scratch using Next.js 15, TypeScript, Tailwind CSS, and React Three Fiber. The 3D portrait now has spring-based smooth tracking.
Jul 17, 2024
Initial Launch
First version of the portfolio launched with vanilla HTML, CSS, and JavaScript. Features include a 3D self-portrait, flip-card project gallery, and responsive design.