Cursors✨ Premium

Emoji Cursor

Five emojis in an array, an index that advances on every click: the moment the mouse enters the zone, the system cursor disappears and a Unicode character takes its place, repositioned on every mousemove to match the pointer's exact coordinates.

JSFun

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

Seasonal campaign for a fashion or beauty store — Emoji Cursor example 1

① Seasonal campaign for a fashion or beauty store

WhenThe store is launching its autumn collection and wants the cursor to reflect the mood on the hero, without an autoplaying video.
WhyFive season-tied emojis (🍂, 🎃, ✨, 🕯️, 🦊) set the atmosphere on first hover. Clicking to change the symbol offers a moment of play before purchase. The cursor returns to normal outside the hero — catalog pages are unaffected.
SettingsSwap the array for the collection's emojis and target a <section> around the hero. Test on Windows: dark emojis (🍂) can vanish on a warm beige background. ✨ and ⭐ hold up on almost any backdrop.
Invitation page for a birthday or private event — Emoji Cursor example 2

② Invitation page for a birthday or private event

WhenA one-off event site wants an interactive signature that can be changed per event without mobilizing a developer for each variant.
WhyEditing a five-entry array is enough to recolor the entire page atmosphere. The emoji cycle on click creates a light moment between two sections. The ephemeral nature of the page makes this playful register acceptable where an institutional site would refuse it.
SettingsFive themed emojis (🎂, 🎉, 🥂, 🎶, 🎊 for a birthday). To cover the whole page, attach listeners to document.body and add cursor: none on body.
Product page for stickers, stationery or children's games — Emoji Cursor example 3

③ Product page for stickers, stationery or children's games

WhenA product aimed at children or teens makes site enjoyment a selling point: the emoji cursor is an immediate signal that the brand does not take itself too seriously.
WhyEmojis are part of this audience's visual language. A cursor that changes on click matches their expectation of interactivity. Be honest about the register: this effect has no place on a B2B portal or a healthcare site.
SettingsFive entries maximum — beyond that the cycle loses memorability. Prefer wide, high-contrast symbols (🦄, 🌈, 🚀, ⭐, 🎯) legible at 24 px across all platforms.

How it works

The effect relies on two separate actors: the detection zone (#zone-emoji, 300 px tall) and a floating element (#cursorEmoji) positioned outside the normal document flow. The moment mouseenter fires on the zone, two things happen at once: the zone receives the cursor-active class — the CSS hook that hides the native cursor via cursor: none — and the emoji element switches from display: none to display: block. The symmetric mouseleave reverses both: class removed, element hidden. The system cursor reappears immediately on exit, no delay.

Position tracking is direct, with no intermediate computation. On every mousemove, the script writes e.clientX into style.left and e.clientY into style.top of the emoji element. No threshold, no easing force: the emoji tracks the pointer pixel for pixel in the viewport's coordinate space. For these absolute coordinates to land correctly, the element must be set to position: fixed in the CSS — that keeps it positioned correctly regardless of scroll depth.

The third listener, click, attached to the zone, handles the rotation. An integer index initialized at 0 advances on each click using the modulus operator: (emojiIndex + 1) % emojis.length. With a five-entry array — ✨ → 🚀 → 💫 → ⭐ → 🔥 → ✨… — the loop never ends. The script updates the element's textContent with the current entry. The index persists between zone entries (but resets to 0 on page reload), so the visitor resumes on the last clicked emoji if they re-enter the zone.

Accessibility

  • The #cursorEmoji element carries a Unicode character that screen readers interpret: '✨' may be announced as 'sparkles' depending on the synthesis engine. Add aria-hidden="true" to this element before deployment — it is a pointer decoration, not information to be read aloud.
  • The emoji rotation fires on click on the zone — not from the keyboard. A keyboard user can interact with child elements inside the zone, but will never see or trigger the symbol change. Never make information or action feedback depend on this mechanism.
  • On touch screens, mouseenter, mousemove and mouseleave do not fire in normal use. The emoji cursor stays hidden and the zone behaves like a plain block. Some mobile browsers emit a simulated mouseenter on tap — test that behavior if the zone contains tappable elements.
  • prefers-reduced-motion has no direct impact: the effect produces no CSS animation in the strict sense. If you add a transition to the emoji's appearance or repositioning, that preference should govern it.

Browser compatibility

The JavaScript syntax is ES2015: arrow functions, const, modulus on a variable — IE 11 halts on a syntax error. The real compatibility factor lies in the OS emoji rendering engine: Apple Color Emoji (macOS/iOS), Segoe UI Emoji (Windows) and Noto Color Emoji (Android/Linux) can produce radically different drawings for the same Unicode character.

Chrome 88+✓ Full
Firefox 87+✓ Full
Safari 14+✓ Full (Apple Color Emoji)
Edge 88+✓ Full (Segoe UI Emoji — differs from macOS rendering)
Mobile iOS✓ Inert (touch)
Android Chrome✓ Inert (touch)

Without JavaScript, <code>#cursorEmoji</code> stays at <code>display: none</code> and the native cursor works normally. On an ES5 engine, a syntax error blocks execution; the zone remains visible and readable. The effect is purely additive: nothing breaks without it.

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
The emojis array ['✨', '🚀', '💫', '⭐', '🔥'] — 5 entries This is the only content list in the script. Replace the characters with your own: the first displays on zone entry (index 0), the rest cycle on click. The modulus adjusts automatically to any length — 3, 8 entries, or 1 alone for a fixed cursor with no rotation.
Initial emoji (emojiIndex = 0) index 0 → '✨' The index is reset to 0 on page reload, not on each zone entry: if the visitor clicks, exits, then re-enters, the cursor resumes from the last emoji shown. To consistently start on a different symbol, change the index's initial value.
Positioning offset emoji's top-left corner = exact cursor coordinates The emoji is placed by its top-left corner at the pointer's coordinates, so the cursor points into the character's corner. A fixed pixel offset shifts the anchor point — useful to center the emoji visually on the cursor tip.
Activation zone (#zone-emoji) a 300 px tall div The listeners are attached to the element targeted by getElementById('zone-emoji'). Swap it for any container in your page. To cover the entire document, target document.body and add cursor: none on body in the CSS.
Display toggle logic (display block / none) instant toggle on mouseenter / mouseleave The emoji appears and disappears without any transition. For a fade, replace the display toggle with an opacity toggle and a CSS transition, keeping display: block permanently. Add pointer-events: none when the element is invisible — otherwise it intercepts clicks.
cursor-active class (on #zone-emoji) added on mouseenter, removed on mouseleave This class carries the cursor: none rule on the zone. If a nested element (a link, a field) needs to restore the native cursor, add cursor: auto directly on that element — it overrides the parent rule without touching the JavaScript.

FAQ

That is expected and documented. Each OS ships its own emoji font: Apple Color Emoji on macOS/iOS, Segoe UI Emoji on Windows, Noto Color Emoji on Android. The same Unicode can produce a very different drawing — 🔥 and 🥰 are well-known examples. If consistent cross-platform rendering matters, replace the characters with 24–32 px PNG or SVG images in the element's content. Otherwise, stick with stable emojis: ✨, ⭐, 🚀 and 🎯 vary little across engines.
Target document.body instead of the dedicated div and attach the listeners to it. Add cursor: none on body in the CSS. The #cursorEmoji element must stay in position: fixed to reposition correctly on scroll. Keeping mouseenter/mouseleave on body makes the emoji disappear when the mouse leaves the browser window — a coherent behavior.
No. The script writes two CSS properties (style.left and style.top) with no heavy computation — no Math.sqrt, no getBoundingClientRect(). On a position: fixed element outside the document flow, the browser handles the update without a page reflow. A requestAnimationFrame wrapper can cap write frequency if needed — but it is rarely necessary for this effect.