SVG✨ Premium

Moving Mask

A soft halo that follows the mouse, a finger or the arrow keys and lets you see, through a radial mask, the layer hidden under the surface.

JSMaskMouse

Updated

This effect is part of Effect.Labs — 840 vanilla effects, some free, some premium. SVG has 30 effects, including 3 free. Explore the category →

3 usage examples

“Find the owl” game: a torch in the forest at night — Moving Mask example 1

① “Find the owl” game: a torch in the forest at night

WhenThe kids' page of a nature park, in its night-walk section: instructions and a counter on the left, a forest plunged in darkness on the right. The halo lights up the pines, the oak and, if you look carefully, the owl hidden in the leaves.
WhyThe whole game lives in the effect's behaviour: you only see what the torch lights. With a finger on a tablet, a child moves the light like a real torch; with the keyboard, the arrows do the same job.
SettingsTwo drawings with the same geometry and .mvm-media: night forest in .mvm-front, lit forest (with the owl) in .mvm-layer. --mvm-w:100%, --mvm-h:100%, --mvm-radius:86px, --mvm-front:#081120, --mvm-layer:#1d3352. The counter switching to 1 / 1 is a small page script comparing the halo position with the owl's.
A cabinetmaker's behind-the-scenes: the workshop plan shows under the sideboard photo — Moving Mask example 2

② A cabinetmaker's behind-the-scenes: the workshop plan shows under the sideboard photo

WhenA cabinetmaker's “Behind the scenes” page: a full-width band shows a walnut sideboard in a room. Wherever the mouse goes, the 1:20 technical drawing appears in blue, dimensions included, as if lifting a corner of the photo.
WhyVisitors find that the finished piece and the plan are the same thing, in the same place: the halo links the object to how it was made without adding a second picture to scroll past.
SettingsPhoto and plan as SVG .mvm-media, same framing. --mvm-w:100%, --mvm-h:202px, --mvm-corner:0 (square-edged band), --mvm-radius:112px for a halo wide enough to read the dimensions, --mvm-layer:#1d4f89.
“Explore the collection” button swept by a glowing halo — Moving Mask example 3

③ “Explore the collection” button swept by a glowing halo

WhenThe top of a rain-jacket brand's homepage: headline, subtitle and a pill-shaped button. Under the mouse, a yellow-orange glow slides along the button and lights up the label.
WhyA button that reacts to the exact pointer position catches the eye without any constant animation: the page stays calm until you come near it.
Settings.mvm-zone set on the <a> link (no tabindex), label repeated in the layer with aria-hidden="true". 270 × 54 px, --mvm-corner:27px, --mvm-radius:58px, --mvm-front:#173653, --mvm-layer:linear-gradient(100deg,#ffd23f,#ff8c42), --mvm-layer-color:#1a1206, --mvm-front-y:center, --mvm-front-pad:0, --mvm-touch:auto (a finger still scrolls the page).

How it works

The .mvm-zone holds two layers: .mvm-front, the visible surface, and .mvm-layer, the hidden layer placed on top. That layer only shows through mask-image: radial-gradient(circle var(--mvm-radius) at var(--mvm-x) var(--mvm-y), #000, transparent) (plus its -webkit- version): solid at the centre of the halo, it fades out gradually up to the radius, 60 px by default. It feels like a torch rather than a sharp hole.

At rest the halo is off (layer at opacity 0): only the surface reads. When the mouse comes in, the script puts the halo centre under the pointer, as a percentage of the zone, and adds the .mvm-on class: the layer fades in over 0.25 s and the halo then follows every move with no lag. It switches off when the mouse leaves. Both layers take your content: text, or images and SVG with the .mvm-media class, which covers the whole layer — that is how you reveal a photo or a plan under another picture.

With a finger, the halo lights up where the finger lands, follows it while dragging and goes out when it lifts; for that, the zone blocks page scrolling (touch-action: none, adjustable with --mvm-touch). With the keyboard, the zone (tabindex="0") lights the halo on focus and moves it with the arrow keys: 5% of the zone per press, 15% with Shift.

Each zone keeps its own position, set as inline style on it: several zones in one root or several copies of the effect work separately. Nothing is added to window.

Accessibility

  • Keyboard: Tab lights the halo (#c4b5fd outline), the arrow keys move it, Shift + arrow goes faster. Arrows only scroll the page when focus is not on the zone.
  • Screen readers: the mask hides nothing from assistive technology: text in both layers is read. If the layer repeats the surface (a button with a doubled label), add aria-hidden="true" to .mvm-layer. A game based on sight (finding an object) should offer its own text equivalent.
  • Reduced motion: under prefers-reduced-motion: reduce the fade-in goes away. The halo stays tied to the user's gesture, which is the interaction itself.
  • Touch: finger down = halo on, finger up = halo off. On a large zone in a scrolling page, set --mvm-touch: pan-y: vertical scrolling keeps working, at the cost of a halo that no longer follows the finger during that scroll.

Browser compatibility

Measured in Chromium 151, Firefox 153 and Playwright's WebKit 26.5: 0 errors; the 2nd copy on the page reacts on its own; keyboard focus and arrows; finger down then dragged (touch pointer events); no animation with reduced motion; nothing overflowing at 390 px. :focus-visible sets the minimum versions; the mask is also written with the -webkit- prefix, which Chrome needs before version 120.

Chrome 86+✓ Full (measured)
Firefox 85+✓ Full (measured)
Safari 15.4+✓ Full (measured in Playwright's WebKit)
Edge 86+✓ Full (Chromium engine)
Safari iOS 15.4+✓ Finger checked in emulation, not on a real device
Chrome Android 86+✓ Full (Chromium engine, finger checked in emulation)

Older browsers: mouse and finger work in older versions that have the inset property (Chrome 87, Firefox 66, Safari 14.1: the versions that brought the inset property used to stack the faces); only lighting up on keyboard focus is missing. Without JavaScript the halo never lights up and only the surface shows.

The code

HTML structure to paste into your page (the CSS + JS come with your purchase):

index.html — structure
<div class="fx-0625-root">
  <div class="mvm-zone" tabindex="0">
    <span class="mvm-front">Move mouse</span>
    <div class="mvm-layer">
      <span>Hidden!</span>
    </div>
  </div>
</div>

One-time payment, no subscription — the code is yours forever

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
--mvm-w and --mvm-h 280px and 180px Zone size (width capped by the container).
--mvm-corner 1rem Zone corner rounding.
--mvm-radius 60px Halo radius. The layer is solid at the centre and fades out up to this radius.
--mvm-front and --mvm-front-color linear-gradient(135deg,#1e1e2e,#2d2d44) and #9a9aa2 Background and text of the visible surface.
--mvm-front-y and --mvm-front-pad flex-end and 0 1rem 1rem Where the surface text sits: at the bottom by default, so it does not overlap the hidden text; center and 0 for a button.
--mvm-layer and --mvm-layer-color linear-gradient(135deg,#6366f1,#8b5cf6,#d946ef) and #fff Background and text of the layer the halo reveals.
--mvm-touch none touch-action value on the zone. none: the finger moves the halo. pan-y or auto: the finger scrolls the page.
--mvm-bg and --mvm-min-h #0a0a0f and 300px Root background and minimum height.
.mvm-media class — On an image, SVG or video inside a layer: the media covers it entirely, any text goes on top.

FAQ

Put the first in .mvm-front and the second in .mvm-layer, each as <img class="mvm-media"> with the same framing. The halo then shows the second picture exactly where the pointer is.
On the zone itself, yes by default: that is what lets the finger move the halo. For a large zone in a long page, set --mvm-touch to pan-y: the page scrolls, and the halo still lights up where the finger lands.
So it does not mix with the layer's text when the halo sits in the middle: the mask gradient is soft, and the two texts used to overlap. Put it back in the centre with --mvm-front-y:center if your layer has no text, or for a button whose layer repeats the label.