

CV and Portfolio OA has two layers. On top, paper: every picture is printed in two inks. Beneath, a dark ground where the same pictures are in colour. There are three roads between them, and the site reads in full without taking any.
- Kind of project
- CV and portfolio
- Structure
- Two layers, one canvas
- Size
- 32 pages in two languages
- Stack
- Next.js, three.js and GSAP
Every page exists twice: printed on paper and lit beneath it.
That is not two designs, and not two sets of pictures. The browser lays out one page, and a single canvas fixed behind it paints each picture both ways. Where the paper opens is up to whoever is reading.
Three roads between the layers.
The pointer leaves a trail that behaves like ink in water: a stable-fluids simulation opens the paper where it passes and closes it a moment later. A ring in the bar spreads the other layer across the whole page from the button itself, in 1.7 seconds. And a first load waits beneath, until the paper rises as one straight-edged sheet.
A column above, a wheel beneath.
On a desktop the index does not scroll. The mouse wheel, the arrow keys or a drag turn an endless reel of eleven covers, which settles with one in the middle. On paper it is a straight column; beneath, the same covers hang on a wheel seen from its rim, each a twelfth of a turn further round. That wheel exists only in the shader: in the DOM the boxes keep to their column.
Browser lays out, canvas paints.
Every picture is an ordinary image in its box, with its alt text and its focus. A single canvas reads those boxes on every frame and paints the printed version or the colour one inside them; the two inks are not a second file, they are worked out from the same image. The type is white and difference-blended with whatever is behind it, so it is always the inverse, even with half a letter in each layer.
Switched off, it still says everything.
Without WebGL, with reduced motion or on a phone, no canvas paints the pictures: what is left is an ordinary page with native images and all of its content. Add ?still to any address and a desktop gets that same page. On a phone the layer change survives: it still spreads from the button, over a background that is loaded only on the first tap.
Two inks computed in the shader, no second file
Type inverted by a blend mode, not by script
Five neutral tokens for every component
A 3.6 million pixel budget for the canvas
The canvas stops drawing when nothing moves
Titles set on the margin by measured ink
The build fails if the two languages drift
A monogram constructed from five numbers