The Feel Lab · 29 specimens

Nothing here makes an app faster. Everything here makes it feel faster.

Modern interface craft is mostly a set of small, borrowed-from-physics lies. A grey box that pretends content is coming. A heart that turns red before the server has agreed. A list of ten thousand rows that only ever draws twenty. Every specimen below has a switch. Turn it off, watch the same interaction happen with the trick removed, and feel the difference in your gut rather than read about it.

29specimens
0ms actually saved
100%felt difference
⌘Ktry it anywhere

Waiting

The user's clock and the machine's clock are different clocks. These nine specimens all cheat the first one.

01

Skeleton screens

A spinner says "nothing is here yet." A grey ghost of the layout says "it's already arriving, this is its shape." Same 1,400ms wait, different verdict.

wait: 1400ms either way
The shimmer sweep matters as much as the boxes. Static grey reads as frozen; moving grey reads as working.
02

Optimistic UI

Paint the success before the server confirms it, then quietly reconcile. If the request fails, roll the interface back and say so. Every like button you have ever pressed works this way.

likes: 128 idle
Round trip is 900ms. With the switch off you sit and watch it. With it on the cost only shows up when the network actually breaks.
03

Blur-up images

Ship a thumbnail so small it fits in the HTML — roughly a kilobyte — blur it hard, then cross-fade the real file over the top. The reader sees the colour and composition immediately and never faces an empty rectangle.

full file arrives at 1200ms
Both canvases are drawn from the same source. The blurred one is a 60×40 render, which is the whole point.
04

Prefetch on hover

The gap between your cursor arriving on a link and your finger completing the click is roughly 200–300ms of free time. Spend it fetching the page. The click then resolves against something already in memory.

cold
nothing loaded yet
Page costs 700ms to fetch. Hover long enough and the click costs 0ms — the work was done while you were still deciding.
05

Streaming the page

Rather than holding the whole response until the slowest query finishes, the server flushes the shell first and fills the holes as each part resolves. The page becomes readable and clickable long before it is complete.

first paint:
Total time is identical at 2200ms. Only the order of arrival changes, and the order of arrival is the entire experience.
06

Virtual lists

Ten thousand rows exist in your data. About twenty exist on screen. Draw those twenty, keep a tall empty spacer so the scrollbar still tells the truth, and recycle as the user scrolls. Gmail, Slack and every feed you use do this.

rows in data: 10,000 rows in the DOM: render:
Switch it off and the same list is rendered in full. Watch the render time, then try to scroll it — that stutter is 10,000 live nodes.
07

Debounce the search box

Firing a request on every keystroke means eleven requests for the word "interfaces", ten of which are already stale by the time they land. Wait for a 300ms pause instead and send one.

requests: 0
Off: one bar per keystroke, in coral. On: one bar per pause, in teal. The interface feels identical to the person typing and the server load is not close.
08

Code splitting, then quiet prefetch

Only ship the JavaScript this screen needs, then use the idle moments to pull down what the user is statistically about to open. The first load stays small and the second navigation costs nothing.

nothing loaded
Settings weighs 600ms. Fetched during the idle seconds after boot, it is already sitting there when the user finally goes looking for it.
09

The meta-trick: keep it in memory

The strongest move is to delete the loading state entirely. Hold the data client-side, render from that instantly, and sync to the server in the background. This is why Linear and Figma feel structurally unlike a website.

Off: every tab refetches at 650ms, forever. On: the first visit costs, every return is free. Flip between the tabs a few times with each setting.

Motion

Good motion is not decoration. It is the interface explaining what just happened to what.

10

Spring physics over easing curves

An easing curve arrives and stops dead. A spring overshoots slightly, settles back, and reads as an object with mass. Your hand has held ten thousand objects with mass and knows the difference immediately.

A
B
A = the setting above · B = linear ease, always
Spring here is a real simulation — stiffness 170, damping 16, integrated per frame — not a cubic-bezier impersonating one.
11

Shared element transitions

When a thumbnail opens, the thumbnail should become the detail view rather than being replaced by it. The eye tracks one continuous object and never has to re-find its place.

Implemented as FLIP: measure first position, jump to last, invert the difference as a transform, then play it back. Four steps, no library.
12

Stagger

Everything animating in at once is one event. The same items offset by 40ms each is a sequence, and a sequence reads as composed rather than dumped.

Keep the total under about 350ms. A stagger long enough to notice as a delay stops being polish and starts being a queue.

Hands

Four patterns the web borrowed back from phones, plus the one power users would riot over.

13

Drag to dismiss

A sheet you can throw away with your thumb, with resistance when you pull the wrong way and a velocity threshold that lets a flick close it early. Closing stops being a target you have to hit.

Some screen behind the sheet.
Share this specimen
Drag the handle downward to dismiss. Let go past halfway, or flick it.
closed
Switched off, the handle is inert and the only way out is the close button — which is how nearly every web modal still behaves.
14

Swipe actions

Hide destructive and frequent actions under the row itself. The list stays clean for reading and stays fast for operating, which are usually two conflicting demands.

Drag a row left with a mouse or a finger. Past 40% it commits; short of that it springs back and nothing happened.
15

Long press

A secondary menu that costs nothing visually because it isn't drawn until you ask. Hold for half a second, feel the progress fill confirm that the interface understood, then choose.

The fill is not decoration. Without visible progress a long press feels like a button that ignored you for half a second.
16

The command palette

One keystroke that reaches every action in the product without a mouse. Linear made it table stakes; for people who use an app daily it is the difference between a tool and a website.

or press ⌘K / Ctrl K anywhere on this page
It works from anywhere on this page right now, including inside other demos. That is the property that makes it worth building.

Surfaces

Five ways to stop a screen from looking like a screen.

17

Glass

A blurred, slightly saturated sample of whatever sits behind the panel. It creates depth without a heavy shadow, and it tells you the thing underneath is still there rather than gone.

backdrop-filter: blur(11px) saturate(150%)
Saturation is the part people skip. Blur alone goes grey and muddy; the saturate(150%) is what makes it read as glass rather than fog.
18

Layered shadows

Real light does not cast one shadow at one blur radius. Stack three — tight and dark, medium, wide and faint — and the card stops looking like a rectangle with a grey smudge under it.

ONE SHADOW
THREE SHADOWS
Left is fixed for comparison. Every card on this page uses the stacked version — including this one.
19

Grain

A faint noise layer over a gradient breaks up the banding that betrays a flat digital surface. It is the cheapest way to make a screen feel like a printed object.

Generated by an inline SVG feTurbulence filter — no image request, a few hundred bytes, works at any size.
20

Mesh gradients

Four or five colour points bleeding into each other from different corners, rather than a straight line between two hex values. Organic instead of ruled.

Four stacked radial-gradient layers over a base colour. No canvas, no image, no library.
21

Dark mode that isn't just inverted

Pure black is a hole. Real dark mode sits on a dark grey around #121212, raises cards by getting lighter rather than by adding shadow, and desaturates accents so they stop vibrating against the ground.

#000000
card, elevated by shadow you can't see
#121212
card, elevated by being lighter
On OLED the black one saves power and costs legibility. Elevation-by-lightness is the part almost every hand-rolled dark theme forgets.

Layout & type

Where a page gets its posture.

22

The editorial headline

Oversized, tightly tracked, mixed weights inside a single line so hierarchy happens within the sentence instead of between elements. Gradient fill last, and sparingly.

Ship things that feel impossibly fast

Hover this line to watch weight animate
Negative tracking of about -0.03em at display sizes. At body sizes the same value would be unreadable, which is why it belongs to the headline alone.
23

Bento grid

Cells of deliberately different sizes locked into one grid. Size becomes a way of saying which thing matters, without a single heading.

PRIMARY METRIC
USERS
UPTIME
REVENUE
ACTIVITY
NOTES
Switched off it is a uniform grid: the same six items, no opinion about any of them.
24

Parallax depth

Layers that scroll at different speeds read as layers at different distances. Restraint is the whole game — a few percent of differential is atmosphere, a lot of it is nausea.

depth
Scroll the page while watching this panel. Movement is driven by its own position in the viewport, not by a scroll listener on the window.
25

Scroll-triggered reveal

Elements fade and rise as they cross into view, so a long page reads as a guided walk rather than a wall. You have been experiencing this specimen since you started scrolling.

then scroll back down
One IntersectionObserver for the whole page. A scroll event listener doing the same job would run hundreds of times a second for no reason.

Micro

Four one-second details, each of which someone argued about for a week.

26

Button feedback

A press that compresses two or three percent and brightens slightly confirms the action landed before any result exists. The ripple carries the same message outward from the exact point you touched.

Scale down, never up. Growing under a finger reads as the button escaping the press rather than receiving it.
27

Focus states and floating labels

The label lifts and shrinks into a caption, the border takes the accent, a soft ring appears. Three simultaneous signals that the system is listening to this field and no other.

Keep the ring, always. It is a micro-interaction for mouse users and the only orientation a keyboard user has.
28

Hover cards and counting numbers

A preview on hover removes a whole navigation for information-dense screens. A figure that counts up as it enters view turns a static number into an event worth reading.

@feel_lab
The Feel Lab
joined 2026
Twenty-nine small lies about speed, each with an off switch.
0
specimens shipped this year
Ease the count out, don't run it linearly, and stop at the real figure. A counter still climbing when the eye arrives is noise.
29

Toast, not modal

"Saved" is information, not a decision. Information gets a toast that leaves on its own. A modal demands the user dismiss your success on your behalf, which breaks the flow they were in.

Reserve blocking dialogs for things that are genuinely destructive or genuinely a fork in the road. Confirmation of success is neither.
Saved
Specimen detail

Click anywhere to close and watch it travel home.