Devlog #1
Building my portfolio as an operating system that runs in a browser tab.
Why this exists
When I first saw the brief, I honestly just planned to knock something out — the
minimum that clears the requirements and move on. But somewhere between the first
commit and the second, I changed my mind. If I’m going to build a website anyway,
why not make it the website — my actual portfolio. So I decided to treat
PrismaOS as the real thing and pour everything I have into it: a portfolio site
that is itself the project. Every window, every pixel of the shell, is something
I want to be proud to show. That reframing is why this repo suddenly has design
tokens, four themes, and a genie animation nobody asked for.
The concept: instead of a scrolling landing page, the visitor boots into a tiny
web desktop — a menu bar, a dock, draggable windows — and explores my work the
way you’d poke around someone’s actual computer.
This milestone: the window manager
Devlog #1 covers the skeleton — the part that makes it feel like an OS rather
One source of truth
Every window’s position, size, focus order (z-index), and minimized state lives
in a single hook, useWindows. Components never track their own coordinates.
Opening, closing, focusing, moving, and resizing all route through that one
place.
Dragging without dropping frames
The naive approach — call setState on every pointer move — makes windows stutter
because React re-renders on every pixel. So during a drag I don’t touch state at
all. I mutate the DOM node directly (el.style.left/top) for a smooth 60fps, and
only commit the final position to state when the pointer is released.
The other trick is setPointerCapture. Once the pointer is captured, the drag
keeps working even if the cursor flies outside the window — or outside the
browser entirely. Touch gets the same behavior for free, since Pointer Events
unify mouse and touch.
handle.setPointerCapture(e.pointerId)
// ...move: write to el.style directly (no re-render)
// ...up: commit once via onDragEnd()
Resizing from the bottom-right corner uses the exact same pattern: capture,
mutate the DOM live, commit on release.
Bug I caught
Clicking any window brings it to the front by bumping a z-counter.