webdevpuneet.com

Live Cursor Name Tags — Free HTML CSS JS Snippet

Live Cursor Name Tags · Cards · Plain HTML, CSS & JS · Live preview

CategoryCards

Copy & paste

Source Code

<div class="lcn-card">
  <div class="lcn-head">
    <div>
      <h2>Product spec draft</h2>
      <p>3 people editing</p>
    </div>
    <div class="lcn-avatars">
      <span class="lcn-avatar" style="background:#f97316">JM</span>
      <span class="lcn-avatar" style="background:#22d3ee">RS</span>
      <span class="lcn-avatar" style="background:#a78bfa">TN</span>
    </div>
  </div>

  <div class="lcn-doc" id="lcnDoc">
    <div class="lcn-line w1"></div>
    <div class="lcn-line w2"></div>
    <div class="lcn-line w3"></div>
    <div class="lcn-block"></div>
    <div class="lcn-line w2"></div>
    <div class="lcn-line w4"></div>
    <div class="lcn-line w1"></div>
    <div class="lcn-line w3"></div>

    <div class="lcn-cursor" id="cursor1" style="--c:#f97316"><svg viewBox="0 0 24 24" fill="#f97316"><path d="M4 2l14 8-6 2-2 6z"/></svg><span class="lcn-tag" style="background:#f97316">Jamie</span></div>
    <div class="lcn-cursor" id="cursor2" style="--c:#22d3ee"><svg viewBox="0 0 24 24" fill="#22d3ee"><path d="M4 2l14 8-6 2-2 6z"/></svg><span class="lcn-tag" style="background:#22d3ee">Rosa</span></div>
    <div class="lcn-cursor" id="cursor3" style="--c:#a78bfa"><svg viewBox="0 0 24 24" fill="#a78bfa"><path d="M4 2l14 8-6 2-2 6z"/></svg><span class="lcn-tag" style="background:#a78bfa">Theo</span></div>
  </div>
  <p class="lcn-note">Simulated cursors for demo purposes — no live connection.</p>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Pre-scripted waypoint paths
Each cursor loops through a small array of points instead of moving randomly.
Time-based interpolation
pointAt() derives position purely from elapsed time — no drift or accumulation error.
requestAnimationFrame loop
Syncs to the browser's repaint cycle and pauses automatically on hidden tabs.
Transform-only movement
Cursors move via translate(), never triggering layout or paint of the document.
Staggered speeds and offsets
Three cursors move independently so the scene never looks synchronized or repetitive.
Percentage-based coordinates
Paths are defined as percentages of the doc area, so they stay correct at any size.
Color-coded name tags
Each cursor's tag color matches its pointer and the collaborator's avatar.
Honest demo labeling
A visible note clarifies these are simulated cursors, not a live connection.

About this UI Snippet

Live Cursor Name Tags — Simulated Multiplayer Cursors on a Shared Document

Screenshot of the Live Cursor Name Tags snippet rendered live

Figma, Google Docs, and Notion all share a visual signature: colored cursors with floating name tags drifting around the canvas as collaborators work. This snippet recreates that visual pattern — not a real multiplayer backend, but the exact look and motion — using plain CSS and requestAnimationFrame, so you can see and reuse the animation technique without standing up WebSockets or a presence server. Pair it with multiplayer cursors for a closer look at cursor rendering, or collaborator presence bar for who's-online UI.

Pre-scripted paths, not random motion

Each cursor follows a small array of waypoint percentages (points) that loop back to the start, rather than random jitter. Random motion reads as noisy and unrealistic; a handful of deliberate waypoints reads as a person actually scrolling and clicking through a document, which is closer to what real presence cursors look like in practice.

One time-based interpolation function

pointAt() takes the array of waypoints and a progress value t between 0 and 1, finds which segment t falls into, and linearly interpolates between that segment's two points. Because position is a pure function of time rather than accumulated per-frame movement, the animation can never drift, stutter, or accumulate floating-point error — reloading or resizing the page just re-samples the same path at the current time.

Driven by requestAnimationFrame, not setInterval

A single tick() loop reads the current timestamp, computes each cursor's position along its path, and sets a CSS transform: translate() — the one property that animates without triggering layout or paint of surrounding content. requestAnimationFrame syncs the loop to the browser's repaint cycle, so motion stays smooth and automatically pauses when the tab isn't visible, unlike a fixed-interval timer.

Staggered offsets keep it readable

Each cursor's offset shifts where it starts along its loop and each has a different speed, so the three cursors never move in lockstep or cross paths predictably — mirroring how real collaborators work at different paces in different parts of a document, which keeps the scene visually interesting rather than looking like one animation copy-pasted three times.

Reusing this for real presence

To make this real, replace the pre-scripted paths with actual cursor coordinates broadcast over a WebSocket or a service like Liveblocks/Pusher — the rendering half (the .lcn-cursor markup, the transform-based positioning, and the name tag styling) stays exactly the same; only the source of x/y changes from a scripted path to a network message.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA document mockup renders with three colored cursors already gliding around it.
  2. 2
    Watch the pathsEach cursor loops through its own waypoints at a different speed and offset.
  3. 3
    Resize the windowCursor positions are read as percentages of the doc area, so they stay correctly placed.
  4. 4
    Adjust a pathEdit a cursor's points array to change its route around the document.
  5. 5
    Change the paceTune each cursor's speed value to make it drift faster or slower.
  6. 6
    Wire to real dataSwap pointAt()'s scripted lookup for live x/y coordinates from your presence backend.

Real-world uses

Common Use Cases

Collaborative editor demos
Show the multiplayer feel of a document tool without a backend, with coloured pointers and name tags gliding along pre-scripted waypoint paths.
Design tool marketing
Pair with multiplayer cursors to show how teammates move around a shared canvas, using transform-only movement that never triggers layout.
Onboarding illustrations
Demonstrate what live collaboration looks like to new users, with pointAt() deriving each position purely from elapsed time.
Presence feature previews
Combine with a team presence list so people see both who is online and where they are working.
Empty states and timing learning
Animate cursors across an empty document, and study path interpolation inside a requestAnimationFrame loop that pauses automatically.
Related: User Role Card with Conditional Permission Checkboxes
See the User Role Card with Conditional Permission Checkboxes for a related cards pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

No — this snippet simulates the visual pattern with pre-scripted paths and requestAnimationFrame, with no network connection or backend. It's meant to demonstrate and let you reuse the rendering and motion technique. For real presence, you'd broadcast actual cursor coordinates over a WebSocket or a service like Liveblocks and feed them into the same transform-based rendering.

Random per-frame jitter looks noisy and mechanical. A small loop of deliberate waypoints, interpolated smoothly between them, reads as a person actually navigating a document — closer to the motion real presence cursors exhibit in tools like Figma and Google Docs, which is the visual effect this snippet is recreating.

pointAt() derives each cursor's position as a pure function of the current timestamp and its path, rather than adding a small delta every frame. That means there's nothing to accumulate or drift — the animation is always correct no matter how much time passed since the last frame, and it can't stutter if a frame is dropped.

requestAnimationFrame automatically throttles or pauses when a tab is backgrounded, which is the correct, battery-friendly behavior. Because position is computed from the current timestamp rather than accumulated increments, the cursors resume smoothly at the correct position when the tab regains focus instead of jumping or catching up.

Keep the .lcn-cursor markup and its transform-based positioning exactly as is, but replace the pointAt()/PATHS logic with a handler that receives real x/y coordinates from your backend (WebSocket, Liveblocks, Pusher, etc.), converts them to the same percentage-of-container or pixel space, and sets the same translate() transform. Add and remove cursor elements dynamically as collaborators join and leave.