Skip to content

Presenter Modes

Enable real-time viewport synchronization for presentations and classroom scenarios.

Overview

Presenter Modes provide powerful tools for teachers to control and monitor student viewports during collaborative sessions. Two complementary modes enable different teaching scenarios:

ModeDirectionUse Case
Follow MeTeacher → StudentsPresentations, demonstrations, guided lessons
SpectateStudent → TeacherStudent monitoring, individual help, assessment

Both modes integrate seamlessly with Frames for structured presentations and work especially well in educational settings.

Key Benefits

  • Real-time synchronization at 20 updates/sec (Follow Me) or 10 updates/sec (Spectate)
  • Flexible control with breakaway mechanism (students can opt-out)
  • Privacy options including stealth spectating
  • Integration with Frames for presentation-style navigation
  • Minimal latency using WebSocket transport

Follow Me Mode

How It Works

When a teacher enables Follow Me mode, their viewport (scroll position and zoom level) is broadcast to all participants. Students' viewports automatically synchronize to match the teacher's view in real-time.

┌─────────────────────────────────────────────────────┐
│              FOLLOW ME MODE FLOW                    │
├─────────────────────────────────────────────────────┤
│                                                      │
│  Teacher moves viewport                             │
│      │                                               │
│      ├─► WebSocket broadcast                        │
│      │                                               │
│      ▼                                               │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐            │
│  │Student 1│  │Student 2│  │Student 3│            │
│  │ syncs   │  │ syncs   │  │ syncs   │            │
│  └─────────┘  └─────────┘  └─────────┘            │
│                                                      │
└─────────────────────────────────────────────────────┘

Use Cases

  • Classroom Presentations: Teacher explains content while students follow along
  • Step-by-step Tutorials: Guide students through complex diagrams or workflows
  • Virtual Lectures: Ensure all remote students see the same content simultaneously
  • Group Demonstrations: Show solutions to problems with automatic viewport control

Starting Follow Me

Teachers can enable Follow Me mode through the Teacher Panel UI or programmatically via WebSocket:

javascript
// Connect to board WebSocket
const socket = io('https://api.boardapi.io/whiteboard', {
  query: { token: 'your-jwt-token' }
});

// Join the board
socket.emit('board:join', { boardUuid: 'your-board-uuid' });

// Start Follow Me mode
socket.emit('followme:start', {
  boardId: 'your-board-uuid',
  allowBreakaway: true,      // Let students opt-out
  smoothAnimation: true       // Smooth viewport transitions
});

Configuration Options

typescript
interface FollowMeConfig {
  enabled: boolean;          // Mode is active
  allowBreakaway: boolean;   // Students can disconnect
  showFollowerCount: boolean; // Display "12/15 following"
  syncInterval: number;      // Update frequency in ms (default: 50)
  smoothAnimation: boolean;  // Smooth scroll transitions
}

Broadcasting Viewport Updates

As the teacher moves their viewport, the client should send throttled updates:

javascript
// Throttle to ~20 updates/sec (every 50ms)
let lastUpdate = 0;
const THROTTLE_MS = 50;

function onViewportChange(scrollX, scrollY, zoom) {
  const now = Date.now();
  if (now - lastUpdate < THROTTLE_MS) return;

  lastUpdate = now;
  socket.emit('followme:viewport', {
    boardId: 'your-board-uuid',
    scrollX: scrollX,
    scrollY: scrollY,
    zoom: zoom
  });
}

Receiving Viewport Updates (Students)

Students listen for viewport updates and synchronize their view:

javascript
// Listen for Follow Me mode activation
socket.on('followme:mode', (data) => {
  if (data.enabled) {
    console.log(`Following ${data.leaderName}`);
    // Show "Following..." banner
    showFollowBanner(data.leaderName);
  } else {
    console.log('Follow Me mode stopped');
    hideFollowBanner();
  }
});

// Listen for viewport updates
socket.on('followme:viewport', (data) => {
  // Sync viewport to match teacher
  syncViewport({
    scrollX: data.scrollX,
    scrollY: data.scrollY,
    zoom: data.zoom
  });
});

Breakaway Mechanism

If allowBreakaway is enabled, students can temporarily disconnect from Follow Me mode:

javascript
// Student breaks away (manual scroll/zoom)
socket.emit('followme:breakaway', {
  boardId: 'your-board-uuid'
});

// Student rejoins
socket.emit('followme:rejoin', {
  boardId: 'your-board-uuid'
});

The teacher receives status updates showing who is following:

javascript
socket.on('followme:status', (data) => {
  console.log('Following:', data.following); // ['user-1', 'user-2']
  console.log('Breakaway:', data.breakaway); // ['user-3']
  // Update UI to show "12/15 following"
});

Stopping Follow Me

javascript
socket.emit('followme:stop', {
  boardId: 'your-board-uuid'
});

// All students receive:
// followme:mode { enabled: false, ... }

Spectate Mode

How It Works

Spectate mode inverts the viewport control: the teacher selects a specific student and receives their viewport updates. This enables the teacher to see exactly what the student is viewing and working on.

┌─────────────────────────────────────────────────────┐
│              SPECTATE MODE FLOW                     │
├─────────────────────────────────────────────────────┤
│                                                      │
│  Student works independently                        │
│      │                                               │
│      ├─► Viewport updates                           │
│      │                                               │
│      ▼                                               │
│  Teacher's view syncs to student                    │
│  (Teacher sees student's viewport)                  │
│                                                      │
└─────────────────────────────────────────────────────┘

Use Cases

  • Individual Help: Teacher monitors struggling student's work
  • Assessment Monitoring: Discreetly observe student during exams
  • Progress Checking: Quick view of what each student is doing
  • Remote Tutoring: Follow student's thought process in real-time

Starting Spectate

Teacher initiates spectating by selecting a target student:

javascript
// Start spectating a student
socket.emit('spectate:start', {
  boardId: 'your-board-uuid',
  targetParticipantId: 'student-user-id',
  stealthMode: false  // Student sees "Teacher is watching" indicator
});

Stealth Mode

When stealthMode: true, the student does NOT receive a notification that they're being observed:

javascript
// Stealth spectating (student unaware)
socket.emit('spectate:start', {
  boardId: 'your-board-uuid',
  targetParticipantId: 'student-user-id',
  stealthMode: true
});

Use stealth mode responsibly: Inform students about monitoring policies in advance for ethical transparency.

Student Sends Viewport Updates

The spectated student's client automatically sends viewport updates (throttled to ~10/sec):

javascript
// Student sends viewport updates when being spectated
let lastSpectateUpdate = 0;
const SPECTATE_THROTTLE_MS = 100; // 10 updates/sec

function onStudentViewportChange(scrollX, scrollY, zoom, cursorX, cursorY) {
  const now = Date.now();
  if (now - lastSpectateUpdate < SPECTATE_THROTTLE_MS) return;

  lastSpectateUpdate = now;
  socket.emit('spectate:viewport', {
    boardId: 'your-board-uuid',
    scrollX: scrollX,
    scrollY: scrollY,
    zoom: zoom,
    cursorX: cursorX,  // Optional: student cursor position
    cursorY: cursorY
  });
}

Teacher Receives Viewport Updates

Teacher's client listens for student viewport updates:

javascript
// Listen for spectate viewport updates
socket.on('spectate:viewport', (data) => {
  console.log(`Spectating student ${data.targetId}`);

  // Sync teacher's viewport to student's view
  syncViewport({
    scrollX: data.scrollX,
    scrollY: data.scrollY,
    zoom: data.zoom
  });

  // Optionally show student's cursor position
  if (data.cursorX && data.cursorY) {
    showRemoteCursor(data.cursorX, data.cursorY);
  }
});

Stopping Spectate

javascript
socket.emit('spectate:stop', {
  boardId: 'your-board-uuid'
});

// Student receives (if not in stealth mode):
// spectate:end { observerId: 'teacher-id', ... }

Configuration Options

typescript
interface SpectateConfig {
  targetParticipantId: string; // Student to observe
  stealthMode: boolean;        // Hidden observation
  showCursor: boolean;         // Show teacher cursor to student
  canInteract: boolean;        // Teacher can draw (co-edit mode)
}

Note: showCursor and canInteract are advanced options. Currently, spectate mode is viewport-only by default.

Integration with Frames

Presenter Modes work seamlessly with Frames for structured presentations.

Follow Me + Frames

When Follow Me mode is active alongside Frame navigation:

javascript
// Teacher navigates to a frame
socket.emit('frame:navigate', {
  boardId: 'your-board-uuid',
  frameId: 'frame-uuid',
  animate: true
});

// All following students automatically:
// 1. Receive frame:navigated event
// 2. Receive followme:viewport event syncing to frame bounds

Combined Flow:

  1. Teacher clicks "Next Frame" in presentation mode
  2. Frame navigation event → students' viewports scroll to frame
  3. Follow Me ensures students stay synced even within the frame

Spectate + Frames

Spectate mode shows which frame the student is currently viewing:

javascript
socket.on('spectate:viewport', (data) => {
  // Check if student is within a known frame
  const currentFrame = getFrameAtPosition(data.scrollX, data.scrollY);

  if (currentFrame) {
    console.log(`Student is in frame: ${currentFrame.name}`);
    // Show in UI: "Spectating Ivan - Frame: Practice Exercise"
  } else {
    console.log('Student is outside frames');
  }
});

Lock Modes + Presenter Modes

Frame Lock ModeFollow MeSpectate
noneWorks freelyWorks freely
softWorks, overrides soft warningWorks freely
hardWorks, overrides lock (teacher control)Student cannot leave, spectate shows locked frame

Hard lock behavior: When a frame is locked with hard mode, students cannot leave even if they break away from Follow Me. The teacher can still navigate them via Follow Me, which takes precedence.

Complete Example: Virtual Classroom

This example demonstrates a complete virtual classroom workflow using Follow Me and Spectate:

javascript
// Teacher Dashboard Component
class TeacherDashboard {
  constructor(socket, boardId) {
    this.socket = socket;
    this.boardId = boardId;
    this.followMeActive = false;
    this.spectatingStudent = null;
  }

  // Start presentation mode
  startPresentation() {
    this.socket.emit('followme:start', {
      boardId: this.boardId,
      allowBreakaway: true,
      smoothAnimation: true
    });
    this.followMeActive = true;

    // Listen for follower status
    this.socket.on('followme:status', (data) => {
      this.updateFollowerCount(data.following.length, data.breakaway.length);
    });
  }

  // Navigate to next slide
  nextSlide(frameId) {
    this.socket.emit('frame:navigate', {
      boardId: this.boardId,
      frameId: frameId,
      animate: true
    });
    // Follow Me automatically syncs students to this frame
  }

  // Monitor specific student
  spectateStudent(studentId) {
    // Stop spectating previous student if any
    if (this.spectatingStudent) {
      this.socket.emit('spectate:stop', { boardId: this.boardId });
    }

    // Start spectating new student
    this.socket.emit('spectate:start', {
      boardId: this.boardId,
      targetParticipantId: studentId,
      stealthMode: false
    });

    this.spectatingStudent = studentId;

    // Receive student's viewport
    this.socket.on('spectate:viewport', (data) => {
      this.syncToStudentView(data);
    });
  }

  // Stop spectating and return to presentation
  stopSpectate() {
    this.socket.emit('spectate:stop', { boardId: this.boardId });
    this.spectatingStudent = null;
  }

  // End presentation
  endPresentation() {
    this.socket.emit('followme:stop', { boardId: this.boardId });
    this.followMeActive = false;
  }
}

// Student Client Component
class StudentBoard {
  constructor(socket, boardId) {
    this.socket = socket;
    this.boardId = boardId;
    this.isFollowing = false;
    this.isBeingSpectated = false;

    this.setupListeners();
  }

  setupListeners() {
    // Handle Follow Me mode
    this.socket.on('followme:mode', (data) => {
      if (data.enabled) {
        this.isFollowing = true;
        this.showBanner(`Following ${data.leaderName}`);
      } else {
        this.isFollowing = false;
        this.hideBanner();
      }
    });

    // Sync viewport in Follow Me
    this.socket.on('followme:viewport', (data) => {
      if (this.isFollowing) {
        this.syncViewport(data.scrollX, data.scrollY, data.zoom);
      }
    });

    // Handle being spectated
    this.socket.on('spectate:start', (data) => {
      if (!data.stealthMode) {
        this.isBeingSpectated = true;
        this.showSpectateIndicator(data.observerName);
      }
    });

    this.socket.on('spectate:end', () => {
      this.isBeingSpectated = false;
      this.hideSpectateIndicator();
    });
  }

  // Student manually breaks away
  breakAway() {
    this.socket.emit('followme:breakaway', { boardId: this.boardId });
    this.isFollowing = false;
    this.showRejoinButton();
  }

  // Student rejoins Follow Me
  rejoin() {
    this.socket.emit('followme:rejoin', { boardId: this.boardId });
    this.isFollowing = true;
    this.hideRejoinButton();
  }

  // Send viewport updates when being spectated
  onViewportChange(scrollX, scrollY, zoom, cursorX, cursorY) {
    if (this.isBeingSpectated) {
      this.socket.emit('spectate:viewport', {
        boardId: this.boardId,
        scrollX, scrollY, zoom, cursorX, cursorY
      });
    }
  }
}

WebSocket Events Reference

Follow Me Events

Client → Server:

EventPayloadDescription
followme:start{ boardId, allowBreakaway, smoothAnimation }Teacher starts Follow Me
followme:stop{ boardId }Teacher stops Follow Me
followme:viewport{ boardId, scrollX, scrollY, zoom }Teacher sends viewport update
followme:breakaway{ boardId }Student disconnects from Follow Me
followme:rejoin{ boardId }Student reconnects to Follow Me

Server → Client:

EventPayloadDescription
followme:mode{ enabled, leaderId, leaderName, config }Follow Me mode changed
followme:viewport{ scrollX, scrollY, zoom, timestamp }Viewport update from teacher
followme:status{ following[], breakaway[] }Follower status update

Spectate Events

Client → Server:

EventPayloadDescription
spectate:start{ boardId, targetParticipantId, stealthMode }Teacher starts spectating
spectate:stop{ boardId }Teacher stops spectating
spectate:viewport{ boardId, scrollX, scrollY, zoom, cursorX, cursorY }Student sends viewport update

Server → Client:

EventPayloadDescription
spectate:start{ observerId, observerName, targetId, stealthMode }Spectating started (sent to student if not stealth)
spectate:viewport{ targetId, scrollX, scrollY, zoom, cursorX, cursorY }Student viewport update (sent to teacher)
spectate:end{ observerId, targetId }Spectating stopped

For complete WebSocket API documentation, see WebSocket API Reference.

Best Practices

Performance

  • Throttle viewport updates: Don't send updates more frequently than 20/sec (Follow Me) or 10/sec (Spectate)
  • Use smooth animations: Enable smoothAnimation: true for better UX
  • Monitor follower count: Show teachers when students break away
  • Cleanup on disconnect: Always stop Follow Me/Spectate when teacher leaves

User Experience

  • Visual indicators: Show clear "Following Teacher" or "Teacher Watching" banners
  • Breakaway button: Make it easy for students to opt-out when needed
  • Rejoin option: Provide a "Return to Presentation" button for breakaway students
  • Frame integration: Combine with Frames for structured presentations

Privacy & Ethics

  • Inform students: Disclose monitoring capabilities in your privacy policy
  • Stealth mode sparingly: Use stealth spectating only when pedagogically justified
  • Clear indicators: When not in stealth, show visible "Being Watched" notifications
  • Parent consent: For minors, ensure parental consent for monitoring features

Accessibility

  • Keyboard controls: Support Escape key to exit spectate/breakaway
  • Screen readers: Announce Follow Me/Spectate mode changes
  • Focus management: Don't override keyboard navigation during Follow Me
  • Contrast: Ensure indicators have sufficient contrast

Common Issues

Students not syncing in Follow Me

Check:

  1. Verify followme:mode event received with enabled: true
  2. Ensure viewport sync function is called on followme:viewport events
  3. Check throttling - updates should arrive ~20/sec
  4. Verify students haven't manually broken away

Spectate viewport not updating

Check:

  1. Student is sending spectate:viewport events (check network tab)
  2. Throttling is correct (~10/sec, not too aggressive)
  3. Teacher is listening to spectate:viewport events
  4. Target student is still connected to the board

Lag or jitter in viewport sync

Solutions:

  • Implement smooth interpolation between viewport positions
  • Increase throttle interval if network is slow (100ms instead of 50ms)
  • Use smoothAnimation: true for Follow Me mode
  • Consider reducing zoom precision to 2 decimal places

Students getting "stuck" in Follow Me

Solution: Implement timeout to auto-break away if teacher disconnects:

javascript
let followMeTimeout;

socket.on('followme:mode', (data) => {
  if (data.enabled) {
    // Reset timeout on each viewport update
    clearTimeout(followMeTimeout);
    followMeTimeout = setTimeout(() => {
      // Teacher hasn't sent updates for 5 seconds
      autoBreakAway();
    }, 5000);
  }
});

Quota & Limits

Presenter Modes are available on specific tiers. Check your organization's plan:

FeatureStarterProfessionalBusinessEnterprise
Follow Me Mode
Spectate Mode
Stealth Spectate
Concurrent Spectate Sessions-15Unlimited

See Quota & Limits API for checking your current limits.

Next Steps

Support

Need help implementing Presenter Modes?