Advertisement

Ebbinghaus Illusion

| by Vladimir | 2 min read | code by Alvaro Montoro
Intermediate

See the Pen Ebbinghaus Illusion.

Tech & Dependencies

CSS

Features

  • Optical Illusion
  • Hover Reveal
  • Single Div
  • Responsive

Browser Support

Chrome 112+ Edge 112+ Firefox 112+ Safari 16.5+

Core

This is a Pure CSS Ebbinghaus Optical Illusion. It uses mathematically sized radial gradients on a single element to trick the brain into perceiving two identical circles as different sizes. Its function is to provide an interactive, visual “gotcha” moment, demonstrating how relative scale affects human perception, revealed instantly via a smooth hover transition.

Specs

  • Weight: < 1 KB. Pure CSS without any HTML markup beyond the body tag.
  • Performance: High. It avoids rendering actual DOM nodes by drawing everything using background-image: radial-gradient(). Movement is handled via translate, ensuring hardware acceleration.
  • Theming / Customization: Easily controlled via CSS custom properties (--p for the center circle, --s for the surrounding circles).
  • Responsiveness: Fluid. Utilizes the modern CSS Media Query Range Syntax (@media(aspect-ratio < 1)) to switch from a horizontal comparison to a vertical one on portrait screens.
  • Graceful Degradation: [!] Heavily reliant on native CSS nesting (&) and Range Syntax. In browsers older than 2023, the styles will fail to parse, leaving a completely blank screen.

Anatomy

  • HTML: Zero markup. The effect is painted directly onto the <body> element.
  • CSS: The engine. The body::before and body::after pseudo-elements each render one half of the illusion. They employ 9 layered radial-gradient backgrounds to draw the 8 surrounding circles and 1 central circle. Custom properties (--size2, --p1, --p2) dynamically calculate the exact placement of each gradient layer.

Logic

The standout feature is the procedural drawing mechanism inside the pseudo-elements, avoiding SVG or multiple HTML nodes.

background:
  radial-gradient(farthest-side circle, var(--s) 99%, #0000 0) var(--p1) var(--p1) / var(--size2) var(--size2),
  /* ... 7 more surrounding circles ... */
  radial-gradient(farthest-side, var(--p) 99%, #0000 0) 50% 50% / var(--size) var(--size);

By defining the background size (var(--size2)) and position (var(--p1), --p2) dynamically based on CSS variables, the author creates a reusable drawing matrix. The ::before element sets --size2 to 1x, drawing tight, small surrounding circles. The ::after element scales --size2 to 2.5x and pushes the position values to the edges (0, 100%), drawing large, distant surrounding circles. The central orange circle remains exactly --size in both.

Feel

Intellectually satisfying. The initial state plays a perfect trick on the eye—the right circle looks undeniably larger. Hovering anywhere on the screen causes both clusters to translate and overlap directly in the center. The revelation is immediate and undeniable. The smooth 1-second transition makes the optical overlap feel like a deliberate, mechanical proof rather than a jarring glitch.

Advertisement