Directive 19: CRT distortion — scanline drift, occasional tear, refresh sweep

Three layered CRT motion effects, all subtle, all GPU-cheap:

1. SCANLINE DRIFT — the scanlines slowly migrate downward by 4px over
   9 seconds, looping. Imperceptible at first glance, builds the
   'this screen is alive' sense over a session.

2. CRT TEAR — every 13s the scanline layer briefly shifts horizontally
   by 1-2px at four irregular moments (18.5%, 47.5%, 72.6-73%, 91.5%
   of cycle). The 72.6% tear also dims opacity slightly — a stronger
   'glitch' beat among smaller ones. Cadence designed to feel random,
   not metronomic.

3. REFRESH SWEEP — a 180px-tall faint amber band rolls from top to
   bottom of the viewport every 17 seconds. Mimics the CRT refresh
   pass. Opacity capped at 0.045 — present, not distracting.

All three use prime-ish durations (9, 13, 17) so the layered effects
never sync back into the same pattern within a session.
This commit is contained in:
timevans666
2026-05-21 23:14:32 +00:00
parent 40a1d88f66
commit 430792ecdd
@@ -82,7 +82,7 @@ body::before {
linear-gradient(180deg, rgba(255,152,24,0.015), transparent 30%, rgba(0,0,0,0.25));
}
/* CRT scanlines — overlaid on everything, very subtle */
/* CRT scanlines — overlaid on everything, slow drift + occasional horizontal tear */
body::after {
content:"";
position:fixed; inset:0; pointer-events:none; z-index:1000;
@@ -95,6 +95,55 @@ body::after {
);
mix-blend-mode: multiply;
opacity: 0.7;
animation: scanline-drift 9s linear infinite, crt-tear 13s infinite;
will-change: transform, background-position;
}
/* Scanlines drift slowly downward — barely perceptible, just enough to feel alive */
@keyframes scanline-drift {
from { background-position: 0 0; }
to { background-position: 0 -4px; }
}
/* Occasional one-pixel horizontal tear at irregular intervals — CRT signal drift */
@keyframes crt-tear {
0%, 18%, 19%, 47%, 48%, 72.4%, 73.2%, 91%, 92%, 100% {
transform: translateX(0);
opacity: 0.7;
}
18.5% { transform: translateX(1px); }
47.5% { transform: translateX(-1px); }
72.6%, 73% {
transform: translateX(2px);
opacity: 0.55; /* this tear also dims slightly — stronger glitch moment */
}
91.5% { transform: translateX(-1px); }
}
/* Slow rolling refresh sweep — a faint amber band passes top to bottom every 17s */
html::before {
content: "";
position: fixed;
left: 0; right: 0;
top: 0;
height: 180px;
pointer-events: none;
z-index: 999;
background: linear-gradient(
180deg,
transparent 0%,
rgba(255, 180, 80, 0.02) 30%,
rgba(255, 200, 120, 0.045) 50%,
rgba(255, 180, 80, 0.02) 70%,
transparent 100%
);
animation: crt-refresh-sweep 17s linear infinite;
will-change: transform;
}
@keyframes crt-refresh-sweep {
0% { transform: translateY(-180px); }
100% { transform: translateY(100vh); }
}
/* ============================================================