Cursors✨ Premium

Glow Effect

A distinct glowing orb follows the pointer inside the zone: `mouseenter` reveals it via `display: block`, every `mousemove` repositions it with a fixed 40 px offset on each axis, `mouseleave` hides it — and the zone receives `cursor-active` so CSS can erase the native cursor.

JSGlow

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. Cursors has 26 effects, including 1 free. Explore the category →

3 usage examples

Dark hero on a landing page for a creative tool — Glow Effect example 1

① Dark hero on a landing page for a creative tool

WhenA dark-mode home page for a visual generator, audio editor, or photo app where the hero carries the whole brand identity.
WhyReplacing the OS cursor with a glowing orb as soon as the visitor enters the hero immediately sets the tone: they feel they are in a crafted environment. The orb draws the eye toward the CTA without underlining it graphically.
SettingsApply cursor: none inside .cursor-active; style #cursorGlow with border-radius: 50%, filter: blur(20px), and a color matching your accent; keep display: none → block for a clean on-entry snap.
Black-and-white photo gallery — monochrome portfolio — Glow Effect example 2

② Black-and-white photo gallery — monochrome portfolio

WhenA portfolio or artwork gallery on a very dark background where the image speaks for itself.
WhyThe orb adds a luminous presence without altering the background. It follows the pointer like a torch in the dark — a physical metaphor that adapts naturally to browsing a dark gallery without disturbing the compositions.
SettingsLarger orb (120 × 120 px, offset 60 px); warm color (amber, ivory) to contrast with black and white; keep the native cursor visible to preserve text legibility on hover.
Game interface or dark-mode data cockpit — Glow Effect example 3

③ Game interface or dark-mode data cockpit

WhenA game home screen, selection menu, or dense technical dashboard in dark mode where a generic OS cursor breaks immersion or visual consistency.
WhyThe orb is built to substitute the cursor, not accompany it. A custom luminous reticle reinforces graphic coherence. The binary appearance (<code>display: none → block</code>) fits the rigidity of a technical UI.
SettingsCompact orb (40 × 40 px, offset 20 px); inline SVG shape rather than border-radius: 50%; cursor: none mandatory; light CSS pulse conditioned on prefers-reduced-motion.

How it works

The effect strictly separates boundary and cursor. #zone-glow defines the stage: dark background, one-rem border-radius, and crucially overflow: hidden — the silent boundary that clips the orb at its edges without extra JS. #cursorGlow is a separate DOM element, outside the zone's flow, display: none by default. Only it moves; the zone never does. This role split sets the effect apart from a radial-gradient background: no layer recomputed each frame — a positioned element tracking the pointer.

Three listeners, three strict roles. mouseenter runs two instructions: add cursor-active to #zone-glow (CSS hook for cursor: none), then flip #cursorGlow to display: block — instant appearance, no fade. mouseleave reverses the pair in the same order. Between the two, mousemove rewrites the position on every event: cursorGlow.style.left = e.clientX - 40 + 'px' and cursorGlow.style.top = e.clientY - 40 + 'px'. Two inline style assignments per move — no requestAnimationFrame, no getBoundingClientRect(). The update is synchronous with every pointer movement.

clientX and clientY measure the pointer position in the viewport, not inside the zone. Subtracting 40 px on each axis centers an 80 × 80 px orb on the cursor: the offset equals half the orb's dimensions, and #cursorGlow must be in position: fixed so that left: clientX − 40px is valid regardless of page scroll. overflow: hidden clips the orb visually at the zone's edges without making it a CSS child of the zone. Change the orb's size and you must update the offsets to width / 2 and height / 2.

Accessibility

  • prefers-reduced-motion: no transition or @keyframes in the base snippet. Real use of #cursorGlow almost always involves a filter: blur() or a pulse — gate those behind @media (prefers-reduced-motion: reduce). The repositioning via left/top is not CSS animation and ignores motion media queries: to cover sensitive visitors, disable the mousemove listener entirely when the preference is active.
  • The orb stays display: none on touch screens: no mouseenter or mousemove fires in normal touch use. If you apply cursor: none inside .cursor-active, be careful: some hybrid browsers fire a synthetic hover on first tap. Gate cursor-active behind matchMedia('(hover: hover) and (pointer: fine)') for hybrid environments.
  • None of the three listeners responds to keyboard events: Tab, Enter, Space work normally. If you use cursor: none inside .cursor-active, ensure the rule disappears as soon as the pointer leaves the zone. Define a strong :focus-visible state on interactive elements — the glow orb is not a visual substitute for keyboard navigation.
  • #cursorGlow is purely decorative — no text, no role. Add aria-hidden="true" to it: a div flipping from display: none to display: block can be announced by some AT tools without it. Never carry information through the glow itself — it is invisible to a significant portion of your visitors.

Browser compatibility

The effect uses only basic DOM: getElementById, classList.add/remove, and assignments to style.left/style.top/style.display. No custom properties. The only floor is syntactic: arrow functions require ES2015 — IE 11 halts before the first mouseenter.

Chrome 88+✓ Full
Firefox 87+✓ Full
Safari 14+✓ Full
Edge 88+✓ Full
Mobile iOS✓ Inert (no cursor)
Android Chrome✓ Inert (no cursor)

Without JavaScript, <code>#cursorGlow</code> stays <code>display: none</code>: the zone renders normally, the native cursor stays visible. On an ES5 engine, the syntax error stops the script before any listener is attached — the same clean fallback. The effect is purely additive.

The code

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

index.html — structure
🔒 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
Centering offset (40 in the JS) 40 px — for an 80 × 80 px orb The value 40 appears twice: e.clientX - 40 and e.clientY - 40. It must equal half the orb's dimensions. A 120 × 60 px orb requires asymmetric offsets (60 and 30). An incorrect offset miscenters the halo and exposes the mechanics.
Display toggle (display: none → block) display: none / display: block display is binary and cannot be transitioned. For a fade, replace it with opacity + visibility and transition: opacity .2s: visibility: hidden removes the orb from pointer events during invisibility, preventing stray mousemove events if the zone and orb overlap.
cursor-active class on #zone-glow class with no style imposed by the JS The JS adds and removes cursor-active in mirror with the orb's visibility; your CSS decides what to do. Main use: #zone-glow.cursor-active { cursor: none } to hide the native cursor. Also useful to change the zone's border-color or trigger a CSS animation on the background.
Orb identifier (#cursorGlow) getElementById('cursorGlow') The orb is fetched by id at startup; absent, the script exits cleanly (if (!cursorGlow) return). Update the id in both JS and HTML. Shape, size, color, and animations all live in your CSS — the JS only knows the id, the display state, and the left/top coordinates.
Listening boundary (#zone-glow) 300 px tall div with overflow: hidden All three listeners are attached to #zone-glow. Change its dimensions or border-radius: overflow: hidden clips the orb differently, the JS mechanics stay untouched. To cover several zones, share one #cursorGlow — see FAQ.
mouseenter and mouseleave presence both active, strict mirror Removing mouseleave leaves the orb visible once the pointer has entered the zone — useful when the zone covers most of the viewport. Keeping only mousemove without the other two creates an orb that tracks but never displays: you also need to switch the initial display to block.

FAQ

clientX / clientY give the viewport position, independent of scroll. pageX includes scroll — the orb would drift down. offsetX varies based on the actual target element: a child of #zone-glow would shift the reference and cause jumps. clientX − 40 stays stable because #cursorGlow in position: fixed is also viewport-relative.
display: none → block is binary and cannot be transitioned. For a fade, initialize #cursorGlow with opacity: 0; visibility: hidden; transition: opacity .2s. On mouseenter, set opacity: 1; visibility: visible; on mouseleave, reverse. visibility: hidden preserves the element's dimensions but removes it from pointer events during invisibility, preventing conflicts if the zone and orb overlap.
Keep a single #cursorGlow in the DOM and attach the same listener triplet to each zone: the orb appears and repositions in whichever zone the pointer enters, hides on mouseleave. If zones do not overlap, the transition is natural. If they do, an active-zone counter prevents the first zone's mouseleave from hiding the orb while it has already entered the second.