Skip to content
← components

Loading Beam

new

A laser-beam loading bar: a fill that races to 100% on an eased, jittery curve with a flickering glow and a blurred light at the leading edge. Pure CSS. From basement.fun.

Usage

example.tsx
import { LoadingBeam } from "~/components/loaders/loading-beam";
<div className="w-80"><LoadingBeam /></div>

A loader that lies convincingly

Real loads don't fill at a constant rate, they lurch: a burst, a pause, a crawl near the end. This bar fakes that with a hand-tuned width keyframe track that stutters forward instead of ramping linearly, which reads as "something is actually happening" rather than a metronome.

Two glows sell the laser look, each on its own clock: the bar's box-shadow flickers, and a blurred dot pinned to the leading edge (a ::after) pulses at a different rate. The mismatch is the point. Synchronized glows look mechanical; offset ones look alive. It's three keyframe sets and no JS.

loading-beam.css
/* the width keyframes don't ramp linearly — they stutter, like a real load */
@keyframes beam-main {
0% { width: 0%; } 5% { width: 10%; } 7.5% { width: 15%; }
15% { width: 30%; } 30% { width: 45%; } 50% { width: 70%; }
/* …slows toward the end… */ 90% { width: 95%; } 100% { width: 100%; }
}
/* a blurred dot rides the leading edge, flickering on its own clock */
.loading-beam::after { left: calc(100% - 0.5rem); filter: blur(1.5rem); }

From basement.fun, a project I work on at B3.

The full implementation, across 2 files. Copy it in or grab the zip. It leans on a cn() class helper and the theme tokens (--primary, --border, …).

components/loaders/loading-beam.tsx
import { cn } from "~/lib/utils";
/**
* A "laser beam" loading bar: a fill that races to 100% on an eased, jittery
* curve with a flickering glow and a blurred light at the leading edge. Pure
* CSS. Extracted from basement.fun.
*/
export function LoadingBeam({ className }: { className?: string }) {
return (
<div
className={cn(
"relative h-2 w-full rounded-full bg-white/10",
className,
)}
>
<div className="loading-beam" />
</div>
);
}
app/styles/app.css
/* ── Loading beam (laser loader), from basement.fun ──────────────────────── */
.loading-beam {
position: relative;
height: 100%;
border-radius: 9999px;
background: linear-gradient(90deg, rgba(180, 180, 180, 1) 60%, rgba(255, 255, 255, 1) 100%);
width: 0%;
box-shadow: 0 0 0 0 rgba(180, 180, 180, 0.5);
animation:
beam-main 5s ease-out infinite,
beam-shadow-flicker 1s linear 1s infinite;
}
.loading-beam::after {
content: "";
position: absolute;
top: 50%;
left: calc(100% - 0.5rem);
width: 1.75rem;
height: 1.75rem;
background: radial-gradient(circle, rgba(255, 255, 255, 1) 20%, rgba(0, 0, 0, 0) 100%);
transform: translate(-50%, -50%);
filter: blur(1.5rem);
opacity: 0;
animation: beam-after-flicker 0.5s linear infinite;
}
@keyframes beam-main {
0% { width: 0%; }
5% { width: 10%; }
7.5% { width: 15%; }
15% { width: 30%; }
17.5% { width: 35%; }
19% { width: 40%; }
30% { width: 45%; }
35% { width: 50%; }
40% { width: 55%; }
42.5% { width: 60%; }
45% { width: 65%; }
50% { width: 70%; }
60% { width: 75%; }
62.5% { width: 77.5%; }
67.5% { width: 80%; }
75% { width: 85%; }
85% { width: 90%; }
87.5% { width: 92.5%; }
90% { width: 95%; }
100% { width: 100%; }
}
@keyframes beam-shadow-flicker {
0% { opacity: 0.75; box-shadow: 0 0 12px 2px rgba(180, 180, 180, 0.5); }
35% { opacity: 1; box-shadow: 0 0 12px 3px rgba(180, 180, 180, 0.5); }
50% { opacity: 0.95; box-shadow: 0 0 12px 3px rgba(180, 180, 180, 0.575); }
62.5% { opacity: 1; box-shadow: 0 0 12px 1px rgba(180, 180, 180, 0.45); }
100% { opacity: 1; box-shadow: 0 0 12px 2px rgba(180, 180, 180, 0.5); }
}
@keyframes beam-after-flicker {
0% { filter: blur(1.5rem); opacity: 0.75; }
35% { filter: blur(1.55rem); opacity: 1; }
62.5% { filter: blur(1.55rem); opacity: 1; }
100% { filter: blur(1.5rem); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
.loading-beam { animation: beam-main 5s ease-out infinite; }
.loading-beam::after { animation: none; }
}