Loaders✨ Premium

Glitch Text

The word LOADING at 32 px bold: a red copy of the top half shaking ±2 px every 0.3 s, a static cyan copy of the bottom half — pure CSS, zero JavaScript.

CSSGlitch

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Loaders has 29 effects, including 4 free. Explore the category →

3 usage examples

Online game matchmaking — “SEARCHING” at the centre of the lobby — Glitch Text example 1

① Online game matchmaking — “SEARCHING” at the centre of the lobby

WhenA player has queued for a ranked match; the lobby is looking for nine other players of the same rank and shows, under the word, the players-found counter (3 / 10) with the team slots.
WhyThis is the one loader whose chromatic aberration — red top half shaking ±2 px, cyan bottom half frozen — is the expected look here rather than a compromise: a competitive lobby speaks that language, where a spinner or three dots would look like a brochure site. Kept at its native size (2 rem = 32 px), the mid-height split and the 2 px shift read clearly — which no 16 px reduction inside a button allows.
SettingsWord changed to SEARCHING via content: attr(data-text) (otherwise 3 edits: HTML + both content rules), font-size: 2rem unchanged, letter-spacing: .16em, main colour #6366f1, original red and cyan, animation: 0.3s and ±2 px amplitude unchanged.
Build indicator — editor status bar — Glitch Text example 2

② Build indicator — editor status bar

WhenIn a web IDE or terminal (dark theme), a compilation or lint run is in progress; the status bar shows the current state.
WhyThe 800-weight sans-serif and red/cyan channels sit naturally in the visual language of the terminal and error output. Pure CSS integrates with no library into a native HTML editor.
Settingsfont-family: monospace, font-size: 1.2rem, color: #0f0 (terminal green); red and cyan unchanged.
Security terminal — “DECRYPTING” a live stream — Glitch Text example 3

③ Security terminal — “DECRYPTING” a live stream

WhenAn operator has opened an encrypted tunnel and starts decrypting a 184-block stream; the terminal reports block by block without being able to promise a duration.
WhyThe word split into two halves — the top one jumping in red, the bottom one locked in cyan — literally depicts a signal that is still scrambled in one part and already rebuilt in the other: no other loader looks like data being decrypted. A progress bar would promise a percentage a continuous stream does not know; at 2 rem monospace the word spans a terminal line and the 50% cut stays crisp.
SettingsWord changed to DECRYPTING via content: attr(data-text), font-family: monospace, font-size: 2rem unchanged, main colour #e2e8f0 (terminal white), letter-spacing: .1em; original red, cyan, ±2 px amplitude and 0.3 s duration.

How it works

The .loader-glitch is a div containing the text LOADING at 32 px, weight 800, color #6366f1 (indigo), position: relative. Two pseudo-elements, ::before and ::after, share position: absolute; left: 0; top: 0 and both carry content: “LOADING”hard-coded in the CSS, not via attr(data-text). The ::before is red (color: red) and clipped to the top half with clip-path: inset(0 0 50%); the ::after is cyan (color: #0ff) and clipped to the bottom half with clip-path: inset(50% 0 0). The three layers overlap within the same bounding box, measured at 148 × 37 px with the default sans-serif font.

The glitchLoader animation applies only to ::before (the red top half): 0.3s linear infinite alternate-reverse. The keyframes move that layer via translate through five positions: (0, 0) at the ends, (−2 px, +2 px) at 20%, (−2 px, −2 px) at 40%, (+2 px, +2 px) at 60%, (+2 px, −2 px) at 80%. The alternate-reverse direction plays the sequence backward on odd cycles, avoiding a visible reset. Measured: animationName: “glitchLoader” on ::before, animationName: “none” on ::afterthe cyan bottom half is static, always aligned with the main text.

Two structural points. First: the displayed word appears in three places — the div innerHTML, ::before { content: “LOADING” } and ::after { content: “LOADING” } — without an attr(data-text) binding; changing the label requires three edits. Second: no @media (prefers-reduced-motion) rule is present; measured under reducedMotion: reduce emulation, the animationPlayState of ::before stays “running”. The code contains no JavaScript — the js field is an empty string.

Accessibility

  • prefers-reduced-motion not handled: measured under emulation, the animation keeps running. Add @media (prefers-reduced-motion: reduce) { .loader-glitch::before { animation: none; } } to freeze the effect.
  • This element is meant to signal an ongoing operation: the code declares no role="status", aria-live or aria-busy. Add role="status" aria-live="polite" aria-label="Loading" to the .loader-glitch element.
  • The ::before and ::after pseudo-elements with content: "LOADING" are exposed by some screen readers (NVDA, VoiceOver iOS). To avoid triple reading, add speak: none on the pseudo-elements via CSS — or replace both content values with empty strings and keep the readable text only in the div.
  • The three colors (indigo, red, cyan) are decorative and carry no status information. On a dark background (#0a0a0f), all contrast ratios exceed 4.5:1; on a light background, check each hue independently.

Browser compatibility

Requires clip-path: inset(), @keyframes with transform: translate() and CSS pseudo-elements. Zero JavaScript dependency.

Chrome 55+✓ Full
Firefox 54+✓ Full
Safari 9.1+✓ Full
Edge 79+✓ Full
iOS Safari 9.3+✓ Full
Android Chrome✓ Full

Without clip-path (IE 11), the pseudo-elements render without clipping: red and cyan cover the full text, the jitter persists but the top/bottom split disappears.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="loader-glitch">LOADING</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
Displayed word (HTML + CSS ::before { content: "…" } and ::after { content: "…" }) LOADING Three occurrences to edit together: the div innerHTML and both content rules. To edit in one place only, replace both content values with attr(data-text) and set the attribute on the element.
Main color (CSS color: #6366f1) #6366f1 (indigo) Color of the base text, visible at the lateral edges of the letters where pseudo-elements do not cover.
Top channel color (CSS ::before { color: red }) red — rgb(255, 0, 0) Color of the top half, the only layer that moves. #ff00aa for a magenta/cyan glitch, lime for a terminal look.
Bottom channel color (CSS ::after { color: #0ff }) #0ff (cyan) Color of the static bottom half. To animate it as well, add animation: glitchLoader 0.3s linear infinite alternate to ::after.
Animation duration (CSS animation: glitchLoader 0.3s) 0.3s 0.1s = rapid jitter (urgency); 0.6s = slow glitch (atmosphere); beyond 1 s, the motion reads more like a vibration than a glitch.
Shift amplitude (CSS translate(-2px, 2px) in the keyframes) ±2 px Edit the eight translate() values across the five keyframe stops. ±1 px = subtle glitch; ±5 px = strong distortion.
Split point (CSS clip-path: inset(0 0 50%) and inset(50% 0 0)) 50% — cut at mid-height inset(0 0 30%) / inset(70% 0 0) gives more animated red and less static cyan. The two values must sum to 100%.

FAQ

The ::after has no animation property in the sold CSS — measured: animationName: “none”. Only ::before (red, top half) receives glitchLoader. To animate the bottom half as well, add .loader-glitch::after { animation: glitchLoader 0.3s linear infinite alternate; } — use alternate (not alternate-reverse) so the two channels do not always move in the same direction.
Three places: the innerHTML of div.loader-glitch, then .loader-glitch::before { content: “…” } and .loader-glitch::after { content: “…” } in the CSS. For a single edit point, replace both content values with content: attr(data-text) and set data-text="LOADING" on the element.
fx-0631 uses content: attr(data-text) (text configurable via an attribute, no CSS edit needed), duplicates the text as full-height layers with different clip bands per layer, and is designed as a permanent decorative effect on any text. fx-0438 is a loading indicator with hard-coded text, splits the layers into top/bottom halves and animates only one of them — producing a different, more asymmetric register.