Imprint

3D Box Grid Background for React

A background wall of boxes that takes an imprint of the pointer. The boxes under it sink back along the z axis to one flat floor, the boxes around them stand in front with their side walls showing, and the dent fills back in slowly behind the pointer. Press to push deeper and roll a ring out across the wall.

  • Background
  • Interaction
  • Canvas
  • 3D

Uses motion, clsx, tailwind-merge

Open in ChatGPTOpen in Claude
cell48px
gap2px
depth48px
reach140px
falloff0.45
tilt0.35
perspective900px
rippletrue
sinkStiffness340
riseStiffness64
riseDamping0.62
rippleSpeed820px/s
<Imprint />

The demo follows as you drag.

Softform 2.0 · now in beta
Interfaces that give a little

Softform turns your design system into components that answer the hand. They press, settle and spring back, out of the box.

Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Imprint: A background wall of boxes that takes an imprint of the pointer. The boxes under it sink back along the z axis to one flat floor, the boxes around them stand in front with their side walls showing, and the dent fills back in slowly behind the pointer. Press to push deeper and roll a ring out across the wall.
This demo, Product Hero: A product hero on the default wall. Move across it and the dent follows with a trail that fills back in; press and hold to push the floor deeper and send a ring out.

1. Install it:
   npx shadcn@latest add @keyframeui/imprint-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/imprint-demo.json instead.)
   It adds the demo, components/imprint-demo.tsx, and the component, components/ui/imprint.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Imprint> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 48): Size of one box, in px.
   - gap (default 2): Width of the seam around each box, in px. `0` gives one seamless surface until it's pressed.
   - depth (default 48): How far the boxes under the pointer sink, in px. Holding the pointer down pushes them 60% further.
   - reach (default 140): Radius of the dent around the pointer, in px.
   - falloff (default 0.45): Shape of the dent, from `0` (one flat floor with a sharp rim) to `1` (a smooth bowl that terraces down).
   - tilt (default 0.35): How far above the wall the viewer sits. Higher shows more of the walls; `0` looks straight on, with only the perspective showing them.
   - perspective (default 900): Distance from the viewer to the wall, in px. Lower makes sunk boxes shrink more and their side walls show further from the center.
   - ripple (default true): Roll a ring out across the wall from every press.
   - sinkStiffness (default 340): Stiffness of the spring a box sinks on. Higher snaps the dent in under the pointer faster.
   - riseStiffness (default 64): Stiffness of the spring a box rises back on. Lower fills the dent in more slowly, so the pointer leaves a longer trail.
   - riseDamping (default 0.62): Damping ratio of the rise. Under `1` a box bobs just past the surface before it settles; `1` and up comes back without the bob.
   - rippleSpeed (default 820): How fast the ring from a press rolls out across the wall, in px per second.

4. Don't change, in components/ui/imprint.tsx:
   - One canvas and one rAF loop draw the whole wall. The loop stops once every box settles or the wall leaves the screen, and pointer events only `wake()` it, so nothing re-renders in React.
   - Each box picks its spring per frame: the stiff, near-critical one while its target is deeper, the soft one that bobs past the surface otherwise. That split is what leaves the trail.
   - Boxes paint deepest first, then the surface, then any box bobbing in front of it, and a side wall is drawn only where a neighbor sits deeper and the wall faces the viewer.
   - The dent aims below the pointer by the tilt, so the floor, seen from above, lands under it.
   - Reduced motion sets each box straight to its target and the press straight to held, with no springs, and no ring rolls out.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/imprint.md

Installation

Add the component on its own with the CLI, or copy the file in by hand.

The CLI writes components/ui/imprint.tsx and installs motion, clsx and tailwind-merge. It needs the @keyframeui registry in components.json; add it once per project.

npx shadcn@latest add @keyframeui/imprint

Examples

Finished sections built with Imprint. Each one installs whole, with the component included.

Studio Card

A studio's closing card in stone, with big boxes and `falloff={0}`: every box in reach drops to the same floor, like a row of keys going down, and the floor glows orange through `accentClassName`.

Kiln Studio
We make the parts people touch.
  • Product design
  • Design systems
  • Motion
  • Prototyping
Start a projectBooking from March
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Imprint: A background wall of boxes that takes an imprint of the pointer. The boxes under it sink back along the z axis to one flat floor, the boxes around them stand in front with their side walls showing, and the dent fills back in slowly behind the pointer. Press to push deeper and roll a ring out across the wall.
This demo, Studio Card: A studio's closing card in stone, with big boxes and `falloff={0}`: every box in reach drops to the same floor, like a row of keys going down, and the floor glows orange through `accentClassName`.

1. Install it:
   npx shadcn@latest add @keyframeui/imprint-studio-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/imprint-studio-demo.json instead.)
   It adds the demo, components/imprint-studio-demo.tsx, and the component, components/ui/imprint.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Imprint> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 48): Size of one box, in px.
   - gap (default 2): Width of the seam around each box, in px. `0` gives one seamless surface until it's pressed.
   - depth (default 48): How far the boxes under the pointer sink, in px. Holding the pointer down pushes them 60% further.
   - reach (default 140): Radius of the dent around the pointer, in px.
   - falloff (default 0.45): Shape of the dent, from `0` (one flat floor with a sharp rim) to `1` (a smooth bowl that terraces down).
   - tilt (default 0.35): How far above the wall the viewer sits. Higher shows more of the walls; `0` looks straight on, with only the perspective showing them.
   - perspective (default 900): Distance from the viewer to the wall, in px. Lower makes sunk boxes shrink more and their side walls show further from the center.
   - ripple (default true): Roll a ring out across the wall from every press.
   - sinkStiffness (default 340): Stiffness of the spring a box sinks on. Higher snaps the dent in under the pointer faster.
   - riseStiffness (default 64): Stiffness of the spring a box rises back on. Lower fills the dent in more slowly, so the pointer leaves a longer trail.
   - riseDamping (default 0.62): Damping ratio of the rise. Under `1` a box bobs just past the surface before it settles; `1` and up comes back without the bob.
   - rippleSpeed (default 820): How fast the ring from a press rolls out across the wall, in px per second.

4. Don't change, in components/ui/imprint.tsx:
   - One canvas and one rAF loop draw the whole wall. The loop stops once every box settles or the wall leaves the screen, and pointer events only `wake()` it, so nothing re-renders in React.
   - Each box picks its spring per frame: the stiff, near-critical one while its target is deeper, the soft one that bobs past the surface otherwise. That split is what leaves the trail.
   - Boxes paint deepest first, then the surface, then any box bobbing in front of it, and a side wall is drawn only where a neighbor sits deeper and the wall faces the viewer.
   - The dent aims below the pointer by the tilt, so the floor, seen from above, lands under it.
   - Reduced motion sets each box straight to its target and the press straight to held, with no springs, and no ring rolls out.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/imprint.md

Not Found

A 404 page on small boxes with `falloff={1}` and a closer `perspective`, so the dent is a smooth bowl that terraces down, and the page itself has sunk.

404

This page sank without a trace

It may have moved, or the link has a typo. Everything else is still on the surface.

Back to the surface
Take this demo to your coding agent.
Add this moment from KeyframeUI to our project and adapt it to our content.

Imprint: A background wall of boxes that takes an imprint of the pointer. The boxes under it sink back along the z axis to one flat floor, the boxes around them stand in front with their side walls showing, and the dent fills back in slowly behind the pointer. Press to push deeper and roll a ring out across the wall.
This demo, Not Found: A 404 page on small boxes with `falloff={1}` and a closer `perspective`, so the dent is a smooth bowl that terraces down, and the page itself has sunk.

1. Install it:
   npx shadcn@latest add @keyframeui/imprint-not-found-demo
   (If the CLI can't find @keyframeui, use https://keyframeui.com/r/imprint-not-found-demo.json instead.)
   It adds the demo, components/imprint-not-found-demo.tsx, and the component, components/ui/imprint.tsx.

2. Make it ours: our copy, data, images and colors. Keep the structure the motion depends on. If we only need the effect, use <Imprint> on its own.

3. These props set its feel. Change them before you change the file:
   - cell (default 48): Size of one box, in px.
   - gap (default 2): Width of the seam around each box, in px. `0` gives one seamless surface until it's pressed.
   - depth (default 48): How far the boxes under the pointer sink, in px. Holding the pointer down pushes them 60% further.
   - reach (default 140): Radius of the dent around the pointer, in px.
   - falloff (default 0.45): Shape of the dent, from `0` (one flat floor with a sharp rim) to `1` (a smooth bowl that terraces down).
   - tilt (default 0.35): How far above the wall the viewer sits. Higher shows more of the walls; `0` looks straight on, with only the perspective showing them.
   - perspective (default 900): Distance from the viewer to the wall, in px. Lower makes sunk boxes shrink more and their side walls show further from the center.
   - ripple (default true): Roll a ring out across the wall from every press.
   - sinkStiffness (default 340): Stiffness of the spring a box sinks on. Higher snaps the dent in under the pointer faster.
   - riseStiffness (default 64): Stiffness of the spring a box rises back on. Lower fills the dent in more slowly, so the pointer leaves a longer trail.
   - riseDamping (default 0.62): Damping ratio of the rise. Under `1` a box bobs just past the surface before it settles; `1` and up comes back without the bob.
   - rippleSpeed (default 820): How fast the ring from a press rolls out across the wall, in px per second.

4. Don't change, in components/ui/imprint.tsx:
   - One canvas and one rAF loop draw the whole wall. The loop stops once every box settles or the wall leaves the screen, and pointer events only `wake()` it, so nothing re-renders in React.
   - Each box picks its spring per frame: the stiff, near-critical one while its target is deeper, the soft one that bobs past the surface otherwise. That split is what leaves the trail.
   - Boxes paint deepest first, then the surface, then any box bobbing in front of it, and a side wall is drawn only where a neighbor sits deeper and the wall faces the viewer.
   - The dent aims below the pointer by the tilt, so the floor, seen from above, lands under it.
   - Reduced motion sets each box straight to its target and the press straight to held, with no springs, and no ring rolls out.
   Its timing is all in the TIMING block at the top of that file.

Docs: https://keyframeui.com/components/imprint.md

Props

children

TypeReact.ReactNode

Content laid over the wall. The pointer still presses the boxes under it.

cell

TypenumberDefault48

Size of one box, in px.

gap

TypenumberDefault2

Width of the seam around each box, in px. 0 gives one seamless surface until it's pressed.

depth

TypenumberDefault48

How far the boxes under the pointer sink, in px. Holding the pointer down pushes them 60% further.

reach

TypenumberDefault140

Radius of the dent around the pointer, in px.

falloff

TypenumberDefault0.45

Shape of the dent, from 0 (one flat floor with a sharp rim) to 1 (a smooth bowl that terraces down).

tilt

TypenumberDefault0.35

How far above the wall the viewer sits. Higher shows more of the walls; 0 looks straight on, with only the perspective showing them.

perspective

TypenumberDefault900

Distance from the viewer to the wall, in px. Lower makes sunk boxes shrink more and their side walls show further from the center.

ripple

TypebooleanDefaulttrue

Roll a ring out across the wall from every press.

sinkStiffness

TypenumberDefault340

Stiffness of the spring a box sinks on. Higher snaps the dent in under the pointer faster.

riseStiffness

TypenumberDefault64

Stiffness of the spring a box rises back on. Lower fills the dent in more slowly, so the pointer leaves a longer trail.

riseDamping

TypenumberDefault0.62

Damping ratio of the rise. Under 1 a box bobs just past the surface before it settles; 1 and up comes back without the bob.

rippleSpeed

TypenumberDefault820

How fast the ring from a press rolls out across the wall, in px per second.

boxClassName

TypestringDefault"text-zinc-100 dark:text-zinc-900"

Classes that set the boxes' color (a text-* color). Seams, walls and the floor are shaded from it, lighter seams on a dark color and darker ones on a light color.

accentClassName

Typestring

Classes that tint the floor of the dent (a text-* color). The deeper a box sinks, the more it takes on. No tint when unset.

canvasClassName

Typestring

Classes for the canvas, e.g. an opacity or a mask that fades the wall out at the edges.

className

Typestring

Classes for the root, which sizes the wall. It clips the canvas, so give it the rounded corners.