Pointer Sharing

Overview

Pointer sharing broadcasts your mouse or touch position to all connected clients via WebSocket. Pointers appear as semi-transparent colored disks overlaid on the score, using each client's assigned color. Your own pointer is also visible locally. This provides a lightweight visual communication channel for conducting, cueing, or indicating specific regions of the score during rehearsal and performance.

Unlike freehand annotations, pointer sharing is ephemeral -- positions are only visible while actively shared and disappear when sharing is disabled or the client disconnects.


Enabling Pointer Sharing

  1. Click the pointer share button in the transport control cluster (rightmost button in the top-right group)
  2. The button highlights in your client color to indicate sharing is active
  3. Move your mouse or touch anywhere on the score
  4. Your pointer appears locally and is broadcast to all connected clients

Click the button again to stop sharing. When you stop, your pointer disk is removed from all screens.


How It Appears

Shared pointers render as:

Your own pointer is visible locally in your color. Each client's pointer is independent. Multiple clients can share simultaneously, with all pointers visible to everyone.


Use Cases

Conducting

A conductor or leader can point to regions of the score to indicate which section performers should prepare or begin playing. The pointer persists at its last position even when not moving, allowing the conductor to hold a position while musicians respond.

Collaborative Rehearsal

During remote or distributed rehearsals, pointer sharing provides spatial awareness between performers. A musician can point to a passage while discussing it over a voice channel.

Teaching and Presentation

When demonstrating score features or explaining notation, pointer sharing allows the presenter's focus to be visible to all viewers.


Technical Details

Coordinate System

Pointer positions use world coordinates -- the same coordinate space as the SVG score's viewBox. This ensures positions are consistent across devices with different screen sizes and resolutions.

The pointer overlay is an SVG element parented inside .oscilla-score-inner with a matching viewBox. Screen coordinates are converted to world units using the SVG's CTM (Current Transformation Matrix):

const pt = svgOverlay.createSVGPoint();
pt.x = e.clientX;
pt.y = e.clientY;
const worldPt = pt.matrixTransform(svgOverlay.getScreenCTM().inverse());

Because the overlay is a child of scrollStage, it moves with the score automatically during playback and manual scrolling.

Message Format

Pointer positions are sent as WebSocket messages in world coordinates:

{
  "type": "pointer_position",
  "clientName": "performer-01",
  "x": 4500.5,
  "y": 280.2,
  "clientColor": "#ff6b6b"
}

To remove a pointer (when sharing is disabled or client disconnects):

{
  "type": "pointer_position",
  "clientName": "performer-01",
  "x": null,
  "y": null
}

Throttling

Position updates are throttled to at most one message every 50ms to avoid flooding the WebSocket connection.

Cleanup

Pointers are automatically removed when:

Pointers remain visible at their last position when the user stops moving -- this is intentional for conducting use cases.


Module Location

public/js/interaction/pointerShare.js


API

import {
  initPointerShare,
  togglePointerShare,
  handlePointerPosition,
  isPointerShareEnabled
} from './interaction/pointerShare.js';

// Initialize on page load (called from app.js)
initPointerShare();

// Toggle sharing on/off
togglePointerShare();

// Check if currently sharing
isPointerShareEnabled(); // returns boolean

// Handle incoming pointer (called from socket.js)
handlePointerPosition({ clientName, x, y, clientColor });

Tip: use ← → or ↑ ↓ to navigate the docs