Skip to content
Korame studio build · 2025

Kepaso

A brand site built around real-time 3D that does not cost you the frame budget

The Kepaso logo  a coffee bean on a dark rounded badge

Capabilities demonstrated

  • Web Development
  • Web Design
  • Full-Stack Development

Stack

  • React 19
  • Vite 5
  • three.js
  • React Three Fiber
  • Motion
  • Tailwind CSS v4
  • Lenis
  • sharp
  • Azure Static Web Apps
Overview

What it is

Kepaso is a single-origin coffee roaster brand site: a long, editorial page that follows one bean from a hillside in Yirgacheffe to the cup, through the craft, the menu, the origins and the cafés.

It is a Korame studio build rather than client work a concept brand used to solve a specific engineering problem properly, which is what makes it worth publishing as a case study. The problem is real-time 3D on a marketing site, and the reason most sites that attempt it feel broken.

Challenge

What made it hard

A rotating 3D object is one of the few things on the web that can look genuinely expensive. It is also one of the easiest ways to destroy a page. A WebGL canvas will happily render at whatever resolution the display asks for, at whatever framerate it can manage, forever including while it is scrolled three screens out of view, on a laptop running on battery.

The naive implementation of this page renders a 4K framebuffer continuously for the entire session. That is why "3D website" and "my fan turned on" tend to arrive in the same sentence. The requirement was a scene that reads as a premium product render and behaves like a static image when nobody is looking at it.

Solution

How we approached it

The bean is generated in code rather than loaded as a model. A sphere is deformed in three passes: squashed into an ellipsoid, then pressed with a gaussian valley along one axis to form the bean's signature crease, then given a slight wave so the crease is not a dead-straight line. The geometry is built once, memoised, and never rebuilt only its transform changes per frame.

That choice removes an asset download entirely. There is no .glb to fetch, no loader, no decode step and no draco dependency; the mesh exists a few milliseconds after the JavaScript parses.

Resolution is then governed by a pixel budget instead of a device pixel ratio. Rather than rendering at 2× on any display that claims it, the canvas measures its own box with a ResizeObserver and solves for the ratio that keeps the total framebuffer at roughly 2.4 million pixels, clamped between 0.75× and 2×. A small canvas on a 4K monitor gets crisp rendering; a full-bleed canvas on the same monitor quietly steps down instead of asking the GPU for four times the work.

Finally the render loop is gated by an IntersectionObserver, so it stops when the canvas leaves the viewport, and by prefers-reduced-motion, which drops the animation entirely rather than slowing it.

Key features

What was built

  • Procedural bean geometry

    The mesh is mathematics, not an asset no model download, no loader, no decode, and it is trivially tunable.

  • Pixel-budget resolution scaling

    A ResizeObserver plus a fixed framebuffer budget replaces the usual fixed DPR multiplier, so GPU cost stays bounded across every display.

  • Visibility-gated render loop

    The scene stops rendering when scrolled out of view, which is most of the session on a long editorial page.

  • Scroll-driven narrative

    Harvest, origins and menu sections are choreographed against scroll position with Motion, with Lenis smoothing the scroll itself.

  • Build-time image preparation

    A sharp-based script processes the journey photography at build time, so responsive variants are generated rather than resized in the browser.

  • Accessibility baked in

    Skip link as the first tab stop, and every animation including the 3D scene respects prefers-reduced-motion.

Engineering

Under the hood

Frontend

React 19 on Vite 5, Tailwind CSS v4 with tokens defined as CSS custom properties, and shadcn-idiom component primitives over Base UI.

3D pipeline

three.js through React Three Fiber. Geometry built with BufferGeometry vertex manipulation and cached with useMemo; per-frame work is limited to transforms inside useFrame.

Motion

Motion for scroll-linked and entrance animation, including a spring-smoothed page read-progress bar driven from useScroll.

Performance strategy

Capped device pixel ratio, off-screen render pause, memoised geometry, and static asset preparation moved to build time via sharp.

Deployment

Static build deployed to Azure Static Web Apps.

Experience & design

How it feels to use

The page is structured as a single vertical narrative rather than a set of marketing modules. Scroll is the only navigation the content needs, so the header stays minimal and the reading position is communicated by a thin progress bar rather than a menu highlight.

Lenis smooths the scroll itself, which matters specifically because the 3D scene and the parallax layers are driven from scroll position an unsmoothed wheel event produces visible stepping in anything tied to it.

Colour is warm and very dark, and the document background is painted by an inline style in the head so the near-black hero does not flash white before the stylesheet arrives.

Outcome

What came out of it

The result is a marketing page carrying a live WebGL scene whose GPU cost is bounded by design: resolution is capped by a pixel budget, rendering stops when the canvas is not visible, and the whole effect is removed rather than degraded for anyone who has asked for reduced motion.

The technique transfers directly. The same pattern generate geometry rather than download it, budget resolution rather than trust the display, and gate the loop on visibility is what makes 3D viable on a commercial site instead of a demo.

Tell us what you are trying to build.

A short conversation is usually enough to tell you whether this is a week of work or a quarter of it and whether you should be building it at all.