Atmosphere✨ Premium

Lightning

Two random-zigzag SVG lightning bolts, double screen flash and violet drop-shadow glow — autonomous setTimeout loop, zero canvas, zero dependency.

JSStorm

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 — Weather app or storm game homepage — Lightning example 1

① Hero / Landing — Weather app or storm game homepage

WhenThe user lands on a real-time weather application or a storm-themed game — the effect fires on load to set the atmosphere immediately.
WhyRandomly repositioned bolts deliver a live feeling without video or heavy assets. The dark blue-night gradient background naturally frames white headline text.
SettingsReduce interval to 1000 + Math.random() * 2000 for a more active storm; always show the second bolt (remove the Math.random() > 0.5 condition); initial delay 500 ms.
Product component — Critical incident alert card in an ops dashboard — Lightning example 2

② Product component — Critical incident alert card in an ops dashboard

WhenA network incident or critical disruption is detected — an alert card appears in the dashboard with the effect in the background to signal severity without extra red text.
WhyRare, dramatic bolts communicate urgency visually. A reduced flash intensity preserves readability of the technical alert text.
SettingsExtend interval to 3000 + Math.random() * 4000 for rarer, more dramatic strikes; reduce flash to rgba(255,255,255,.08) in .lightning-flash.active (CSS).
Storm Game — Full-frame lightning title screen — Lightning example 3

③ Storm Game — Full-frame lightning title screen

WhenThe title or main menu screen of a storm-themed game loads — full-frame lightning bolts and intense flash across the whole scene on every strike.
WhyThe effect runs at full power: full-height bolts, double flash covering the entire surface, saturated violet glow — no context showcases it better than a game sky with no UI constraints.
SettingsInterval at 800 + Math.random() * 1500 for an intense storm; always show the second bolt (remove the Math.random() > 0.5 condition); boost flash to rgba(255,255,255,.45).

How it works

The effect is built on pure DOM and inline SVG, no canvas involved. Two div.lightning-bolt elements (absolute-positioned) each hold an <svg viewBox="0 0 100 100"> with two stacked <path> elements: the outer one draws the bolt body in violet (#a5b4fc, stroke-width 2), the inner one draws a thin white core (stroke-width 1). The CSS class .active triggers a two-layer filter: drop-shadow — close halo (light violet, 10 px) and a wider glow (indigo, 20 px).

generateLightningPath() builds a fresh SVG path on every trigger: starting point M(x, 0) with x ∈ [20, 80], then 6 to 10 segments L(x ± rand×40, y+step) with x clamped to [10, 90]. The accumulated random deflections produce the characteristic zigzag of a natural lightning bolt — a different path every time.

The double screen flash simulates atmospheric reverb: at t = 0 the div.lightning-flash (covers the full scene via inset: 0) transitions to background: rgba(255,255,255,.3) over 50 ms then fades back; a second flash fires at t = 100 ms for 30 ms. Two short pulses that mimic real lightning behavior.

triggerLightning() recurses via setTimeout(triggerLightning, 1500 + Math.random() × 3000) — random intervals between 1.5 s and 4.5 s. The second bolt only appears with 50% probability (Math.random() > 0.5). No requestAnimationFrame is used for the lightning itself — the setTimeout loop is enough.

Accessibility

  • prefers-reduced-motion missing: the code does not check window.matchMedia('(prefers-reduced-motion: reduce)') — the animation starts unconditionally. Fix for WCAG 2.3.3: check the media query in the init block and limit to a single non-looping strike when reduction is active.
  • The .lightning-container carries neither role nor aria-label in the provided HTML — add role="img" aria-label="Random lightning animation" for screen reader users.
  • The div.lightning-bolt and div.lightning-flash elements are not marked aria-hidden="true" — dynamically generated inline SVG may be announced by readers. Add this attribute to both.
  • The .lightning-text is a decorative <span> at the bottom of the scene with no semantic role — replace with <p> or add aria-hidden="true" if purely decorative.
  • No pause control: the animation loops indefinitely, violating WCAG 2.2.2 for any auto-started animation lasting more than 5 seconds. Add a pause button that calls clearTimeout on the timer reference.

Browser compatibility

Uses only inline SVG, CSS transitions, and setTimeout — no canvas, no WebGL. Very broad compatibility including older mobile browsers.

Chrome 60+✓ Full
Firefox 60+✓ Full
Safari 12+✓ Full
Edge 80+✓ Full
Mobile iOS✓ Full
Android Chrome✓ Full

Without inline SVG support (browsers over 10 years old), the <code>div.lightning-bolt</code> elements remain empty — no fatal JS errors, the dark blue gradient background stays visible.

The code

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

index.html — structure
<div class="lightning-container" id="lightningContainer" role="img" aria-label="Random lightning animation">
  <div class="cloud" style="left: 20%; width: 80px; height: 30px;"></div>
  <div class="cloud" style="left: 50%; width: 100px; height: 35px;"></div>
  <div class="cloud" style="left: 75%; width: 70px; height: 25px;"></div>
  <div class="lightning-flash" id="lightningFlash" aria-hidden="true"></div>
  <div class="lightning-bolt" id="lightningBolt1" aria-hidden="true"></div>
  <div class="lightning-bolt" id="lightningBolt2" aria-hidden="true"></div>
  <!-- Optional text or overlay -->
  <span class="lightning-text">Content here</span>
</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
filter: drop-shadow in .lightning-bolt.active (CSS) #a5b4fc 10 px / #6366f1 20 px Bolt glow color and radius. Swap both hex values for another tint — e.g. electric blue: drop-shadow(#38bdf8 0 0 10px) drop-shadow(#0284c7 0 0 20px).
background in .lightning-flash.active (CSS) rgba(255,255,255,.3) Screen flash intensity. Drop to .08 for a subtle dashboard effect, raise to .5 for maximum impact in a full-screen hero.
1500 + Math.random() * 3000 in triggerLightning() (JS) 1 500–4 500 ms Interval between two lightning strikes. 800 + Math.random() * 1200 = intense storm; 5000 + Math.random() * 8000 = distant, rare lightning.
Math.random() > 0.5 in triggerLightning() (JS) 50% probability Condition for the second bolt appearing. Replace with true for two bolts every time, false for a single bolt per strike.
(Math.random() - 0.5) * 40 in generateLightningPath() (JS) ±20 viewBox units Horizontal deflection per segment. * 60 = very jagged bolt; * 15 = near-straight stroke.
6 + Math.floor(Math.random() * 4) in generateLightningPath() (JS) 6–9 segments Number of zigzag segments. 10 + Math.floor(Math.random() * 6) = highly detailed bolt; 4 = simple shape with 4 breaks.
stroke="#a5b4fc" in createLightningBolt() (JS) #a5b4fc (light violet) Color of the outer bolt body in the dynamically generated SVG. Also update stroke="white" (the inner core) for a consistent result.

FAQ

Yes — unlike canvas effects with IntersectionObserver, triggerLightning() continues via setTimeout even off-viewport. To save CPU, add an IntersectionObserver on .lightning-container that stores the timer id and calls clearTimeout on exit, then restarts triggerLightning() on entry.
The effect exposes no public API. Extract the logic into a named function: function triggerLightning() { … setTimeout(triggerLightning, …) }, remove the auto-start setTimeout, then call triggerLightning() manually on a business event (network error, form validation, critical alert).
Not without modification — the code targets the IDs lightningContainer, lightningBolt1, lightningBolt2, and lightningFlash directly. For multiple instances, wrap the logic in a function createLightningEffect(containerId, flashId, bolt1Id, bolt2Id) and call it once per scene with distinct IDs.