Atmosphere✨ Premium

3D Bars

8 CSS bars in 3D perspective animated by sinusoidal synthesis — per-bar oscillating rotateY and a double offset box-shadow simulate depth without canvas.

CSS 3DDepth

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. Atmosphere has 57 effects, including 10 free. Explore the category →

3 usage examples

Hero / Landing — Analytics SaaS presentation page — 3D Bars example 1

① Hero / Landing — Analytics SaaS presentation page

WhenOn the home page of an analytics, monitoring, or BI tool, to instantly convey 'your data in motion' before any text.
WhyThe 3D bars pack the concept of 'live data' into a few pixels — more expressive than a dashboard screenshot, with no external image to load.
SettingsBrand gradient in .bars-3d .bar: linear-gradient(135deg, #0ea5e9, #38bdf8, #0284c7). Doubled perspective: perspective: 1000px on .bars-3d. Reduced amplitude: 30 + value × 60 for more restrained background motion.
Product component — Real-time metrics widget — 3D Bars example 2

② Product component — Real-time metrics widget

WhenInside a dashboard as a compact trend bar next to a key figure (requests/s, revenue/hour, active users).
Why8 bars in an 80 × 120 px container communicate 'activity in progress' without axes or legends — perfect for a dense widget grid.
SettingsCompact container: height: 80px on .bars-3d. Thinner bars: width: 14px and gap: 5px. Calm gradient aligned to brand colors.
Conversion micro-interaction — Import or sync completion confirmation — 3D Bars example 3

③ Conversion micro-interaction — Import or sync completion confirmation

WhenWhen a data import, sync, or processing job finishes successfully, right before the summary screen appears.
WhyA rising burst (heights forced to 100 px for 600 ms via <code>bar.style.height = '100px'</code>, then released) gives visual feedback at completion without a permanent decorative loop.
SettingsGreen gradient to signal success: linear-gradient(135deg, #10b981, #34d399, #059669). Trigger the burst in the completion callback: bars.forEach(b => b.style.height = '100px'), then setTimeout(() => bars.forEach(b => b.style.height = ''), 700).

How it works

The .bars-3d container enables the 3D scene via perspective: 500px and transform-style: preserve-3d. Each bar gets a fixed rotateX(-10deg) — tilting it slightly toward the viewer — and a dynamic rotateY centered on −15°, exposing the left face and creating the illusion of a solid block.

Each frame, getSimulatedAudio(8, time, baseFreq=2) generates 8 values in [0–1] by summing two sinusoids whose frequency increases by +0.5 per bar index, plus 20% random noise. Bar height maps to 20 + value × 80 px (range 20 → 100 px). The different per-bar frequencies break synchrony and produce organic motion.

Visual depth relies on two solid (zero-blur) box-shadow values: rgba(79,70,229,.5) 4px 4px 0 and rgba(79,70,229,.3) 8px 8px 0. These right-and-downward offsets simulate the right lateral face and top of each bar — the same depth a real 3D model would require additional geometry for.

The rotateY oscillation follows -15 + Math.sin(time × 0.002 + i × 0.5) × 5 degrees: the i × 0.5 term phase-shifts each bar by index, producing a progressive left-to-right rotation wave. All page animations share a single requestAnimationFrame; no IntersectionObserver is present — the loop runs continuously.

Accessibility

  • prefers-reduced-motion not implemented: the code does not check this media query — the RAF loop runs indefinitely regardless of the OS setting. Add a check before deploying to an accessible context (e.g. if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;).
  • The .bars-3d element carries no role, aria-label, or aria-hidden. For a purely decorative use, add aria-hidden="true" to the root container so screen readers skip the bars.
  • No pause/stop control is exposed — WCAG 2.2.2 (pause mechanism for any auto-started animation lasting more than 5 s) is not met out of the box.
  • Bars are plain non-focusable <div> elements with no semantic role — correct for pure decoration, but add appropriate semantics if the bars carry real data (e.g. dashboard metrics).
  • Color contrast: the indigo-violet gradient on a #0a0a0f background exceeds the 3:1 ratio required for non-text graphical elements (WCAG 1.4.11 — compliant for a decorative element).

Browser compatibility

Requires CSS 3D transforms (perspective, transform-style, rotateX/Y) and requestAnimationFrame — available since Chrome 36+, Firefox 16+, Safari 9+. Zero external dependencies.

Chrome 90+✓ Full
Firefox 90+✓ Full
Safari 15+✓ Full
Edge 90+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

If JS is disabled, the 8 bars render statically with the inline transforms and heights from the initial HTML — the visual is preserved, frozen. No fatal JS errors.

The code

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

index.html — structure
<div class="demo-preview">
  <div class="bars-3d" id="bars3d" aria-hidden="true">
    <!-- 8 <div class="bar"> created dynamically by the JS -->
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
    <div class="bar"></div>
  </div>
</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
`.bars-3d .bar` — background linear-gradient(135deg, #6366f1, #8b5cf6, #4f46e5) Bar color and gradient direction. Swap the three hex values for your brand palette; the 135° angle orients the light top-left → bottom-right.
`.bars-3d` — perspective 500px CSS 3D scene depth. 300 px = dramatic and exaggerated; 800–1000 px = subtle tilt, nearly flat.
`bars3dCount` (JS, line const bars3dCount = 8) 8 Number of bars created at startup. 4 bars = clean minimal chart; 16 bars = dense visualizer (reduce width and gap accordingly).
`.bars-3d .bar` — width 20px Width of each bar. Combine with gap to balance negative space for the container's total width.
`.bars-3d` — gap 8px Spacing between bars. 4 px = tight and dense; 14 px = airy and minimal.
`time × 0.002` in rotateY (JS, animateBars3d) 0.002 Rotation oscillation speed per bar. 0.001 = slow wave; 0.004 = rapid shimmer.
`20 + data[i] × 80` in animateBars3d (JS) min 20 px, amplitude 80 px Height range. 10 + data[i] × 100 = bars nearly flush at rest, full dynamics; 40 + data[i] × 40 = restrained movement around a fixed midpoint.
`.bars-3d .bar` — box-shadow rgba(79,70,229,.5) 4px 4px 0, rgba(79,70,229,.3) 8px 8px 0 Offset shadows simulating the lateral faces of the 3D block. Increase offsets (6 px / 12 px) for more depth; match the color to keep it consistent with the gradient.

FAQ

No — it is pure CSS: perspective, transform-style: preserve-3d, and rotateX/rotateY on ordinary <div> elements. Depth is reinforced by two solid zero-blur box-shadow values. Zero canvas, zero WebGL, zero external dependencies.
Replace getSimulatedAudio() with your real source — for example a Web Audio API buffer (analyser.getByteFrequencyData(buf)) or a normalized array from your own API. The animateBars3d function only needs an array of values between 0 and 1: adjust height = 20 + data[i] * 80 accordingly.
No — there is no IntersectionObserver in the current implementation. The requestAnimationFrame loop runs continuously. To save CPU when the element is off-screen, attach an observer to .bars-3d that sets a boolean paused flag and returns early from animateBars3d when true.