Hills
Oct 05, 2026
Hills is a concept rebrand of Hills Eyewear's online catalogue. It isn't commissioned by or affiliated with Hills. The whole site is built around a liquid-glass lens: every page is seen through curved glass, every frame opens with a camera move into its photo, and you can explore the whole collection as an endless map.
One lens over every page
A WebGL shader in three.js bends everything through the lens, with a magnifying pull, a slight colour split and a ripple along the curved edge. The colour split takes up to 16 samples, which was enough for a clean edge without hurting the frame rate. I extended the shader so I could control how dark the glass is, make it transparent where there's nothing to draw, and add a bulge for page changes.
The other pages are still normal HTML with their own layout, scrolling and text. Each frame, a canvas redraws their photos exactly where the page put them and bends them through the same lens. I did it this way because a site drawn only on a canvas can't be read by screen readers or search engines, and you can't select its text. Here the text stays real and only the pictures curve.
Opening a frame
Clicking a photo zooms the camera into it. The photo scales with the page instead of changing shape, and the window only widens to the full slide once its neighbours have faded out. Closing plays the same move backwards. Captions follow the lens by running its maths in reverse, so they sit exactly on top of the bent photo.
Page changes go through the glass too. The page swells into the lens, the route changes behind it, and the new page settles out of the same bulge. Because the bulge lives in the same shader, it costs nothing extra and holds 60 frames a second.


The map
Each collection can be explored as an endless field of photos that you pan by dragging, scrolling or with the arrow keys. Every photo is shown whole at the largest size its slot allows, and new ones drift in from the edges through the lens. There are 269 images across 13 models, and repeats are kept as far apart as possible, which is why it feels endless.
Keeping it smooth
Photos are fetched at the size they're drawn, decoded off the main thread, and uploaded to the GPU one per frame, nearest the centre first. One upload per frame keeps frame times flat. The carousel only appears once the photos on screen are sharp, so the first thing you see is the final thing. Shaders are compiled before the first frame, so opening a page doesn't stall.
Phones used to crash on this site, because every collection's pictures were sitting in GPU memory at once. Now only the open project's pictures are loaded and the rest are freed.
The project runs Biome, knip and tsr for code checks, Vitest for unit tests, Playwright with Axe for accessibility, and Lighthouse CI for performance.