Lightning
Two random-zigzag SVG lightning bolts, double screen flash and violet drop-shadow glow — autonomous setTimeout loop, zero canvas, zero dependency.
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



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-containercarries neitherrolenoraria-labelin the provided HTML — addrole="img" aria-label="Random lightning animation"for screen reader users. - The
div.lightning-boltanddiv.lightning-flashelements are not markedaria-hidden="true"— dynamically generated inline SVG may be announced by readers. Add this attribute to both. - The
.lightning-textis a decorative<span>at the bottom of the scene with no semantic role — replace with<p>or addaria-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
clearTimeouton the timer reference.
Browser compatibility
Uses only inline SVG, CSS transitions, and setTimeout — no canvas, no WebGL. Very broad compatibility including older mobile browsers.
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):
<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>
Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.
Customize
Options passed to the API or data-* attributes:
| Option / property | Default | Effect |
|---|---|---|
| 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
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.function triggerLightning() { … setTimeout(triggerLightning, …) }, remove the auto-start setTimeout, then call triggerLightning() manually on a business event (network error, form validation, critical alert).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.