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
- Click the pointer share button in the transport control cluster (rightmost button in the top-right group)
- The button highlights in your client color to indicate sharing is active
- Move your mouse or touch anywhere on the score
- 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:
- 30 world-unit diameter disk (scales with the score)
- Client color at 40% opacity -- each client's pointer uses their assigned color
- Smoothly animated position updates (50ms transition)
- Non-interactive -- pointers don't interfere with score interaction
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:
- The sharing client clicks the button to disable sharing
- The client disconnects from the server
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