Why Another Portfolio
Most developer portfolios feel interchangeable—dark mode, gradient blobs, and scroll-triggered fade-ins. I wanted something that felt intentional. Something closer to architecture than web design.
The direction: Japanese Brutalist. Raw structure, visible grids, generous negative space (ma/間), kanji as visual weight, and a warm, earthy palette instead of the usual dark-on-dark aesthetic.
The Globe: 3D Math in a 2D World
The centerpiece is a wireframe sphere rendered on a 2D canvas. No Three.js. No WebGL. Just fillRect calls for each point to maintain a raw, low-fi aesthetic.
The Mathematics
The sphere uses a Fibonacci spiral to distribute 900 points evenly across the surface. Latitude and longitude rings add structural wireframe lines. The entire system rotates via manual rotation matrices applied to every point, every frame.
function rotateYAxis(x, y, z, angle) {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return [x * cos + z * sin, y, -x * sin + z * cos];
}
Audio Reactivity
When audio is playing, the globe responds to real-time frequency analysis:
- Bass frequencies expand the sphere's radius.
- Treble frequencies increase individual point sizes.
- Beat detection spawns ripple waves that propagate across the surface.
Note: The
AudioContextonly initializes after explicit user interaction to comply with modern browser autoplay policies.
Navigation: Slides, Not Scrolling
Instead of standard scroll-based sections, the portfolio uses a robust slide system with GSAP-powered transitions. Each slide occupies the full viewport, creating a "spatial" feel.
Navigation Methods:
- Keyboard: Arrow keys for precise jumping.
- Mouse: Wheel interaction with a "dwell-at-boundary" pattern to prevent accidental rapid-fire slides.
- Touch: Swipe gestures for mobile parity.
The globe smoothly interpolates (lerps) between positions for each slide — large and centered for the Hero (67% x, 50% y, 32% radius), small and top-right for About (88%, 18%, 14%), bottom-left for Experience (15%, 82%, 16%), and pinned to the right half for Projects, where it becomes a planetarium: each project orbits the sphere as a clickable planet.
Design System in Practice
Color & Typography
The palette is intentionally muted. Background is a warm clay (#f5f0eb), foreground is a soft near-black (#1a1a1a), and the accent is a terracotta (#a35b42) reserved for active states.
| Element | Typeface | Style |
|---|---|---|
| Kanji Display | Noto Sans JP | 900 weight, Massive scale |
| Editorial Headings | Geist | 600 weight, Semi-bold |
| Structural Labels | Geist Mono | Uppercase, tracking-widest |
The Grid
A persistent architectural grid overlays every slide—vertical lines at 8%, 25%, 50%, 75%, and 92% width; horizontal lines at 12%, 38%, 72%, and 88% height. These aren't decorative; they provide the structural rhythm that makes the negative space feel intentional rather than empty.
Lessons Learned
- Canvas Performance: 900 points at 60fps is manageable, but per-point wave displacement calculations pushed the limits. Minimizing trigonometry and avoiding expensive operations per point was essential.
- Embracing "Ma": The hardest design decision was leaving gaps empty. Resisting the impulse to fill every space is what makes the design work.
- Fragile APIs: The Web Audio API is temperamental. Browser policies and suspended states required defensive handling and graceful degradation.
Technical Stack
- Framework: Next.js 16 (App Router)
- Animation: GSAP 3.14
- Styling: Tailwind CSS 4
- Typography: Noto Sans JP (kanji + body), Geist (editorial headings), Geist Mono (labels)
- State: React 19 Server Components
Update — August 2026
Six months on, the slide deck is no longer the whole site. It's the front door to a set of standalone routes, all sharing the same grid, palette, and page-transition loader:
| Route | What it is |
|---|---|
/blog | This. File-system MDX, fully static at build time |
/changelog | Git log parsed at build time, grouped by month |
/market | Live crypto watchlist via a CoinGecko proxy route |
/payme | QRIS + Ethereum payment page |
/ratecard | Freelance scoping wizard |
/pokedex | 図鑑 — a PokéAPI browser, built for the fun of it |
/booking | 予約 — a themed Cal.com embed, wired to the ratecard result |
Two structural things changed to make that work. PageOverlays became the shared layer wrapper so every route gets the grid, crosshair, globe, and audio player without duplicating them. And the navbar grew a More dropdown — on mobile and desktop — because seven routes will not fit in a bar designed for four.
The globe follows along: on non-slide routes it drops into an ambient position (88% x, 15% y, 12% radius), decorative and out of the way.