Skip to content

feature(laser): implement telestrator pattern for laser pointer#7681

Merged
steveruizok merged 20 commits intomainfrom
fix/laser-pointer-telestrator-pattern
Jan 26, 2026
Merged

feature(laser): implement telestrator pattern for laser pointer#7681
steveruizok merged 20 commits intomainfrom
fix/laser-pointer-telestrator-pattern

Conversation

@kostyafarber
Copy link
Copy Markdown
Contributor

@kostyafarber kostyafarber commented Jan 12, 2026

In order to make the laser pointer behave like a telestrator (where all annotations remain visible until the user stops drawing), this PR adds session management to the ScribbleManager. Closes #7579.

Previously, each laser stroke segment would fade independently after a delay, creating a "trailing" effect. Now, all laser strokes in a drawing session persist together and fade simultaneously when the user stops drawing.

Before

2026-01-12 at 17 32 13 - Olive Possum

After

2026-01-12 at 17 30 30 - Yellow Pony

pr-7681-walkthrough.mp4

API changes

  • Added ScribbleManager.endLaserSession() method to manually end the active laser session
  • Added ScribbleManager.isScribbleInLaserSession(scribbleId) method to check if a scribble belongs to the active session
  • Added TldrawOptions.laserSessionTimeoutMs option (default: 2000ms) - duration of inactivity before laser session ends
  • Added TldrawOptions.laserMaxSessionDurationMs option (default: 60000ms) - maximum duration for a laser session
  • Added LaserTool.onExit() lifecycle method (calls endLaserSession() when exiting the tool)

Change type

  • improvement

Test plan

  1. Select the laser tool and draw on the canvas
  2. Observe that all strokes remain visible while drawing
  3. Lift the pointer and wait ~2 seconds
  4. Verify all strokes fade together
  5. Draw multiple separate strokes within 2 seconds
  6. Confirm they all persist as part of the same session
  7. Switch away from laser tool - strokes should fade immediately
  • Unit tests

Release notes

  • Laser pointer now keeps all strokes visible until you stop drawing for 2 seconds (telestrator pattern)

Note

Introduces session-based scribbles and updates the laser tool to keep all strokes visible during a session and fade them together afterward.

  • Adds ScribbleManager session API: startSession, addScribbleToSession, addPointToSession, extendSession, stopSession, clearSession, isSessionActive; updates simple APIs (addScribble, addPoint, stop, reset) to work with sessions
  • Implements grouped fade logic and idle timeouts; exports ScribbleSessionOptions
  • Adds editor.options.laserFadeoutMs (default 500) and uses laserDelayMs as session idle timeout
  • Refactors LaserTool to manage a shared session across strokes; handles cancel/exit and reuse/new session creation
  • Replaces/expands unit tests for ScribbleManager and LaserTool

Written by Cursor Bugbot for commit b865104. This will update automatically on new commits. Configure here.

Add session management to ScribbleManager that keeps all laser strokes
visible while actively drawing. Laser annotations now fade together when
the user stops for 2 seconds (configurable via laserSessionTimeoutMs).

Key changes:
- Track laser scribbles in sessions that persist until idle timeout
- Prevent point removal from laser scribbles during active session
- All session scribbles fade together when session ends
- Remove 5-scribble limit for laser strokes (but maintain for others)
- Add laserSessionTimeoutMs and laserMaxSessionDurationMs options
- End laser session when exiting the laser tool
@vercel
Copy link
Copy Markdown

vercel bot commented Jan 12, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
examples Ready Ready Preview Jan 26, 2026 7:10pm
tldraw-docs Ready Ready Preview Jan 26, 2026 7:10pm
4 Skipped Deployments
Project Deployment Review Updated (UTC)
analytics Ignored Ignored Preview Jan 26, 2026 7:10pm
chat-template Ignored Ignored Preview Jan 26, 2026 7:10pm
tldraw-shader Ignored Ignored Preview Jan 26, 2026 7:10pm
workflow-template Ignored Ignored Preview Jan 26, 2026 7:10pm

Request Review

@huppy-bot huppy-bot bot added the improvement Product improvement label Jan 12, 2026
@huppy-bot
Copy link
Copy Markdown
Contributor

huppy-bot bot commented Jan 12, 2026

API Changes Check Passed

Great! The PR description now includes the required "### API changes" section. This helps reviewers and SDK users understand the impact of your changes.

When a user held the pointer down but stopped moving for 2+ seconds, the session timeout would fire and delete the scribble. If the user resumed moving while still holding the pointer, addPoint would throw an error because the scribble no longer existed.

This fix keeps the laser session alive while the pointer is held by calling extendLaserSession() on every tick in the Lasering state. The timeout now only starts counting after the user lifts their pointer, which is the proper telestrator behavior.

- Add extendLaserSession() public method to ScribbleManager
- Add onTick() handler to Lasering state to extend session
- Add tests for extendLaserSession()
@mimecuvalo
Copy link
Copy Markdown
Member

excited about this one!

  • i think i'm not entirely sold on multiple, separate laser paths sticking around (within a time window). let's play around with this a bit more as to what the right default persistence heuristics are here.
  • interesting mini-bug: because timers stop when switching tabs on browsers, you can have the laser still appear when you switch back to the tab. this was true even in the current version, it's just much more prominent in this version

@mimecuvalo mimecuvalo changed the title fix(laser): implement telestrator pattern for laser pointer feature(laser): implement telestrator pattern for laser pointer Jan 13, 2026
@mimecuvalo
Copy link
Copy Markdown
Member

changed title from fix → feature

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Jan 22, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
multiplayer-template 9148ff6 Jan 26 2026, 06:43 PM

@kostyafarber
Copy link
Copy Markdown
Contributor Author

had a jam session with @mimecuvalo

  • agreed that the default timeout feels better at 1s
  • the tapering (thinner line at the start of the laser) does not feel right imo in this new telestrator laser pattern

I've added these changes

in the previous commit, I had added a configurable global timeout option (e.g since starting a laser session, all scribbles would disappear after 1m no matter what) but think this is not really needed. the thing that drives whether all the lasers stay is the idle timeout.

cursor[bot]

This comment was marked as outdated.

kostyafarber and others added 3 commits January 22, 2026 17:15
Introduces TelestrationManager to manage telestration (laser pointer) sessions, enabling persistent and sequentially fading laser scribbles. Updates Editor to include telestration management, adds related options, and refactors ScribbleManager to delegate laser session logic. Includes comprehensive tests for TelestrationManager and updates ScribbleManager tests accordingly.
Introduces a fadeElapsed property to TelestrationSession to accurately track fade-out progress, and applies a quadratic ease-in curve for smoother point removal during fade-out. Updates tests to clarify front-to-back point removal behavior.
Replaces the TelestrationManager with a new ScribbleSession-based architecture for managing scribbles. The ScribbleManager now supports multiple sessions, improved session and scribble lifecycle handling, and more flexible fade/consumption behaviors. Updates all related tests and removes telestration-related code and exports.
Replaces telestration-related option names (telestrationFadeoutMs, telestrationIdleTimeoutMs) with laser-specific names (laserFadeoutMs, laserDelayMs) throughout the codebase. Updates related tests and removes deprecated laserSessionTimeoutMs. Ensures consistent naming and behavior for laser tool fade and delay settings.
Introduces a clear() method to ScribbleSession for immediate cleanup. Updates LaserTool and its child states to use this method, ensuring sessions are properly cleared and transitions are handled consistently when cancelling.
Introduces detailed tests for the LaserTool, covering state transitions, scribble creation, session management, cancel behavior, tool exit, and edge cases. Also removes the check in TldrawScribble that prevented rendering when a scribble in 'stopping' state had fewer than 2 points, ensuring scribbles are rendered as long as they have points.
// Calculate points to remove
const targetRemoved = Math.floor(easedProgress * this.totalPointsAtFadeStart)
const actuallyRemoved = this.totalPointsAtFadeStart - remainingPoints
const pointsToRemove = Math.max(1, targetRemoved - actuallyRemoved)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ease-in fade curve not followed due to forced minimum

Low Severity

The Math.max(1, targetRemoved - actuallyRemoved) in tickGroupedFade forces at least 1 point to be removed every tick, which defeats the 'ease-in' easing curve. With ease-in (progress²), early ticks should remove very few or zero points (slow start, then accelerate). Instead, short scribbles fade linearly at ~1 point per tick, completing much faster than fadeDurationMs. For example, a 10-point scribble with a 500ms ease-in fade would complete in ~160ms (10 ticks × 16ms).

Fix in Cursor Fix in Web

Inlines the logic from ScribbleSession directly into ScribbleManager, removing the ScribbleSession class and its related types and tests. Updates all usages in the LaserTool and its child states to use the new session ID-based API. Removes now-obsolete tests for ScribbleManager and laser tool behavior.
Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@steveruizok
Copy link
Copy Markdown
Collaborator

Ok, did a bunch of last-mile work on this one.

  • the strokes fade out in order, rather than all at once
  • removed the evaporative fade
  • applied an ease to the fade out so rate of erasure eases in

On the code:

  • introduced sessions to prevent crash when a user would create a new laser mark while fading out
  • refactored scribble manager to not have new telestrator-related data
  • simplified tools

@steveruizok steveruizok added this pull request to the merge queue Jan 26, 2026
Merged via the queue into main with commit 73b91e0 Jan 26, 2026
18 of 19 checks passed
@steveruizok steveruizok deleted the fix/laser-pointer-telestrator-pattern branch January 26, 2026 22:04
github-merge-queue bot pushed a commit that referenced this pull request Jan 28, 2026
In order to provide better stroke termination for the laser tool, this
PR adds a 'complete' state to the scribble lifecycle. This state
represents scribbles that are done being drawn but not yet fading,
allowing taper effects to be applied when the user lifts the pointer.

This builds on the telestrator pattern introduced in #7681.

### Change type

- [x] `improvement`

### Test plan

1. Select the laser tool and draw a stroke on the canvas
2. Lift the pointer to complete the stroke
3. Observe the stroke has proper taper at the end point
4. Verify the stroke fades out normally after the idle timeout

### API changes

- Added `complete` to `TL_SCRIBBLE_STATES` enum
- Added `ScribbleManager.complete(id)` method to mark a scribble as
complete

### Release notes

- Laser pointer strokes now have smoother endings with proper taper when
lifting the pointer
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

improvement Product improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve laser pointer to persist until drawing stops (telestrator pattern)

3 participants