Skip to content

Frames

Frames are rectangular zones on a board that enable presentations, slides, and spatial organization. Think PowerPoint meets infinite canvas - combine the structure of slides with the freedom of a whiteboard.

What Are Frames?

A Frame is a rectangular area with defined boundaries on your infinite canvas. Frames allow you to:

  • Divide a board into logical sections (introduction, practice, discussion)
  • Control viewport navigation for participants (soft/hard locks)
  • Create sequential presentations with "Next/Previous" navigation
  • Clone frames to set up parallel breakout rooms
  • Organize complex content into manageable zones

Visual Concept

┌─────────────────────────────────────────────────────────────────┐
│                    INFINITE CANVAS                              │
│                                                                  │
│   ┌─────────────────┐      ┌─────────────────┐                │
│   │   FRAME 1       │      │   FRAME 2       │                │
│   │ "Introduction"  │      │  "Practice"     │                │
│   │                 │      │                 │                │
│   │  [Components]   │      │  [Components]   │                │
│   │  [Objects]      │      │  [Objects]      │                │
│   └─────────────────┘      └─────────────────┘                │
│                                                                  │
│                    ┌─────────────────┐                          │
│                    │   FRAME 3       │                          │
│                    │  "Discussion"   │                          │
│                    │                 │                          │
│                    │  [Components]   │                          │
│                    └─────────────────┘                          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Why Frames?

Problem: Infinite Canvas Chaos

Without frames, collaborative whiteboards face these challenges:

  1. Lost participants: Students scroll around randomly, can't find the current topic
  2. No structure: No clear beginning/middle/end to lessons
  3. Distraction: Students see everything at once, including future content
  4. No grouping: Can't divide class into parallel workspaces

Solution: Structured Flexibility

Frames provide structure while maintaining the flexibility of an infinite canvas:

  • For Teachers: Control what students see and when
  • For Students: Clear navigation, reduced cognitive load
  • For Presentations: Sequential slides with spatial relationships
  • For Group Work: Isolated zones that don't interfere with each other

Key Concepts

Frame Properties

typescript
interface Frame {
  id: string;                    // UUID
  board_id: string;              // Parent board
  name: string;                  // Display name ("Slide 1", "Introduction")
  order: number;                 // Sequence position (0, 1, 2, ...)
  bounds_x: number;              // X coordinate in pixels
  bounds_y: number;              // Y coordinate in pixels
  bounds_width: number;          // Width in pixels (min: 100)
  bounds_height: number;         // Height in pixels (min: 100)
  lock_mode: 'none' | 'soft' | 'hard'; // Viewport control
  background_color?: string;     // Hex color or null
  visibility: 'active' | 'hidden' | 'archived';
  parent_frame_id?: string;      // For tracking clones
  created_at: string;            // ISO 8601 timestamp
  updated_at: string;
}

Lock Modes

Frames support three levels of viewport control:

ModeBehaviorVisual IndicatorUse Case
noneFree navigation, no restrictionsNoneOpen collaboration, free exploration
softCan escape, but indicator shows "Return to lesson"Orange border on canvasGuided work with flexibility
hardCannot leave frame boundsRed border, pan/zoom disabled outsideExams, strict control, focused tasks

Lock Mode Examples

Soft Lock:

┌───────────────────────────────────────┐
│  ┌─────────────────────────────┐     │
│  │ FRAME: "Practice Exercise"  │     │
│  │ Lock: Soft                  │     │
│  │                             │     │
│  │ [Student can work here]     │     │
│  │                             │     │
│  │ If student scrolls out:     │     │
│  │ ⚠️ "You're outside the      │     │
│  │     lesson frame"           │     │
│  │ [Return to Lesson]          │     │
│  └─────────────────────────────┘     │
└───────────────────────────────────────┘

Hard Lock:

┌───────────────────────────────────────┐
│  ┌─────────────────────────────┐     │
│  │ FRAME: "Exam Question 1"    │     │
│  │ Lock: Hard 🔒               │     │
│  │                             │     │
│  │ [Student works here]        │     │
│  │                             │     │
│  │ Pan/zoom disabled outside   │     │
│  │ Scroll attempts ignored     │     │
│  │                             │     │
│  └─────────────────────────────┘     │
└───────────────────────────────────────┘

Frame Visibility

StateDescriptionWhen to Use
activeVisible to all, included in navigationNormal frames
hiddenNot shown in frame list, but physically presentTemporarily hide content
archivedSoft-deleted, excluded by defaultDeleted frames (restorable)

Use Cases

1. Presentation Mode (Slides)

Create sequential slides for lessons or presentations.

javascript
// Create 5 slides arranged horizontally
const slides = [
  'Introduction',
  'Key Concepts',
  'Practice Exercise',
  'Group Discussion',
  'Summary'
];

for (let i = 0; i < slides.length; i++) {
  await fetch('https://api.boardapi.io/api/v1/boards/board-uuid/frames', {
    method: 'POST',
    headers: {
      'X-API-Key': 'your-api-key',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      name: slides[i],
      order: i,
      bounds_x: 1300 * i,        // Horizontal layout
      bounds_y: 0,
      bounds_width: 1200,
      bounds_height: 800,
      lock_mode: 'soft'
    })
  });
}

Teacher navigation:

javascript
// Navigate all students to next frame
socket.emit('frame:navigate', {
  frameId: 'frame-uuid',
  smooth: true  // Smooth scroll animation
});

Result:

  • Students automatically scroll to the frame
  • Teacher panel shows "12/15 on current frame"
  • "Next/Previous" buttons for easy navigation

2. Breakout Rooms (Cloned Frames)

Clone a frame to create parallel workspaces for groups.

javascript
// Create template frame
const template = await fetch('/api/v1/boards/board-uuid/frames', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Group Work Template',
    bounds_x: 0,
    bounds_y: 0,
    bounds_width: 1200,
    bounds_height: 1000,
    lock_mode: 'soft',
    background_color: '#F0F8FF'
  })
}).then(r => r.json());

// Clone for 3 groups
const groups = ['Blue Team', 'Red Team', 'Green Team'];
for (let i = 0; i < groups.length; i++) {
  await fetch(`/api/v1/boards/board-uuid/frames/${template.id}/clone`, {
    method: 'POST',
    headers: { 'X-API-Key': 'your-api-key', 'Content-Type': 'application/json' },
    body: JSON.stringify({
      offsetX: 1300 * (i + 1),   // Space groups horizontally
      offsetY: 0,
      name: groups[i]
    })
  });
}

Result:

┌────────────────────────────────────────────────────────────┐
│  [Template]   [Blue Team]   [Red Team]   [Green Team]     │
│  ┌──────┐     ┌──────┐     ┌──────┐     ┌──────┐         │
│  │      │     │      │     │      │     │      │         │
│  │      │     │      │     │      │     │      │         │
│  └──────┘     └──────┘     └──────┘     └──────┘         │
│                                                            │
│  Each group works in parallel, teacher sees all           │
└────────────────────────────────────────────────────────────┘

3. Exam Mode (Hard Locks)

Set up isolated exam areas with strict viewport control.

javascript
// Create exam frame with hard lock
const examFrame = await fetch('/api/v1/boards/board-uuid/frames', {
  method: 'POST',
  headers: { 'X-API-Key': 'your-api-key', 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'Exam Question 1',
    order: 0,
    bounds_x: 0,
    bounds_y: 0,
    bounds_width: 1200,
    bounds_height: 800,
    lock_mode: 'hard',           // Cannot escape
    background_color: '#FFF3E0'
  })
}).then(r => r.json());

// Navigate all students to exam frame
socket.emit('frame:navigate', {
  frameId: examFrame.id,
  smooth: false  // Instant navigation for exams
});

// Lock is enforced - students cannot scroll out

Behavior:

  • Students cannot pan/zoom outside frame bounds
  • Scroll attempts are blocked
  • Viewport locked to frame dimensions
  • Teacher can still navigate freely

4. Spatial Organization

Organize complex content into logical zones.

┌──────────────────────────────────────────────────────────┐
│                    PROJECT BOARD                          │
│                                                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌─────────┐ │
│  │ Planning │  │  Design  │  │  Develop │  │  Review │ │
│  │  Frame   │  │  Frame   │  │  Frame   │  │  Frame  │ │
│  └──────────┘  └──────────┘  └──────────┘  └─────────┘ │
│                                                           │
│  ┌────────────────────────────────┐                      │
│  │      Resources Frame           │                      │
│  │   (Documentation, Links)       │                      │
│  └────────────────────────────────┘                      │
└──────────────────────────────────────────────────────────┘

Integration with Presenter Modes

Frames work seamlessly with Follow Me and Spectate modes.

Follow Me + Frames

When Follow Me is active, frame navigation automatically syncs all followers:

javascript
// Teacher has Follow Me enabled
socket.emit('followme:start', {
  boardId: 'board-uuid',
  allowBreakaway: true
});

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

// Result:
// 1. All following students receive frame:navigated event
// 2. Students' viewports scroll to frame bounds
// 3. Subsequent viewport updates keep students synced within frame

Learn more about Presenter Modes →

Spectate + Frames

Spectate mode shows which frame a student is currently viewing:

javascript
socket.on('spectate:viewport', (data) => {
  // Detect frame from student's viewport
  const frames = await getFrames(boardId);
  const currentFrame = frames.find(frame =>
    data.scrollX >= frame.bounds_x &&
    data.scrollX <= frame.bounds_x + frame.bounds_width &&
    data.scrollY >= frame.bounds_y &&
    data.scrollY <= frame.bounds_y + frame.bounds_height
  );

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

API Reference

Create Frame

http
POST /api/v1/boards/{boardId}/frames
Content-Type: application/json
X-API-Key: your-api-key

{
  "name": "Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#F0F8FF"
}

Response:

json
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "board_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "Introduction",
  "order": 0,
  "bounds_x": 0,
  "bounds_y": 0,
  "bounds_width": 1200,
  "bounds_height": 800,
  "lock_mode": "soft",
  "background_color": "#F0F8FF",
  "visibility": "active",
  "created_at": "2025-11-26T10:00:00Z",
  "updated_at": "2025-11-26T10:00:00Z"
}

List Frames

http
GET /api/v1/boards/{boardId}/frames
X-API-Key: your-api-key

Response: Array of frame objects, ordered by order field.

Update Frame

http
PATCH /api/v1/boards/{boardId}/frames/{frameId}
Content-Type: application/json
X-API-Key: your-api-key

{
  "lock_mode": "hard",
  "name": "Exam - Question 1"
}

Use cases:

  • Switch to exam mode (change lock to hard)
  • Reorder frames (update order field)
  • Resize frames (update bounds)
  • Change visibility (hide/show frames)

Clone Frame

http
POST /api/v1/boards/{boardId}/frames/{frameId}/clone
Content-Type: application/json
X-API-Key: your-api-key

{
  "offsetX": 1300,
  "offsetY": 0,
  "name": "Group 1"
}

What gets cloned:

  • Frame properties (bounds, lock_mode, background_color)
  • parent_frame_id set to original frame ID

What does NOT get cloned:

  • Objects inside the frame (clone only copies frame structure)
  • Frame ID (new UUID generated)

Delete Frame (Soft)

http
DELETE /api/v1/boards/{boardId}/frames/{frameId}
X-API-Key: your-api-key

Behavior:

  • Sets visibility to archived
  • Objects inside frame are preserved
  • Can be restored by updating visibility to active

Full API Reference →

WebSocket Events

Frame Navigation

javascript
// Teacher navigates students to frame
socket.emit('frame:navigate', {
  boardId: 'board-uuid',
  frameId: 'frame-uuid',
  smooth: true  // Smooth scroll animation
});

// All participants receive:
socket.on('frame:navigated', (data) => {
  console.log(`Navigated to: ${data.frameName}`);
  // Sync viewport to frame bounds
  scrollToFrame(data.frame.bounds_x, data.frame.bounds_y);
});

Lock Mode Changes

javascript
// Teacher locks frame (real-time)
socket.emit('frame:lock', {
  boardId: 'board-uuid',
  frameId: 'frame-uuid',
  lockMode: 'hard'
});

// Students receive:
socket.on('frame:updated', (data) => {
  if (data.lock_mode === 'hard') {
    enableHardLock(data.frame.bounds);
  }
});

Participant Location Tracking

javascript
// Student reports their location
socket.emit('participant:location', {
  boardId: 'board-uuid',
  frameId: 'current-frame-id',  // or null if outside
  viewportBounds: {
    x: 100, y: 50, width: 1920, height: 1080
  }
});

// Teacher receives (for dashboard):
socket.on('participant:location', (data) => {
  console.log(`${data.participantName} is in ${data.frameName}`);
  updateDashboard(data);
});

WebSocket API Reference →

Best Practices

Frame Layout

Horizontal Layout (Slides):

[Frame 1] [Frame 2] [Frame 3] [Frame 4]
  • Easy "Next/Previous" navigation
  • Natural left-to-right progression
  • Standard presentation flow

Grid Layout (Topics):

[Frame 1] [Frame 2]
[Frame 3] [Frame 4]
  • Non-linear navigation
  • Related topics grouped spatially
  • Allows random access

Vertical Stack (Long Form):

[Frame 1]
[Frame 2]
[Frame 3]
  • Scroll-based progression
  • Good for storytelling
  • Mobile-friendly

Frame Sizing

Standard sizes:

  • Presentation: 1200x800 (3:2 ratio)
  • Widescreen: 1600x900 (16:9 ratio)
  • Work area: 1400x1000 (ample space)
  • Mobile-friendly: 800x600 (fits small screens)

Responsive tip: Use larger frames (1600x1000) for complex content, smaller frames (1000x700) for simple slides.

Lock Mode Guidelines

ScenarioRecommended Lock
Guided lessonsoft - Allow exploration with gentle guidance
Timed examhard - Strict control, no escaping
Free worknone - No restrictions
Practice activitysoft - Students can ask for help outside
Breakout roomssoft or none - Groups navigate freely

Naming Conventions

Good names:

  • "Slide 1: Introduction"
  • "Practice: Fill in Blanks"
  • "Group Discussion - Topic A"
  • "Exam Question 3"

Avoid:

  • "Frame 1" (not descriptive)
  • "asdlkfjasdf" (meaningless)
  • Very long names (truncated in UI)

Performance

  • Limit: 50 frames per board (soft limit, performance tested)
  • Cloning: Cloning is fast (no objects copied), safe to clone many
  • Navigation: Smooth animation adds 300ms, instant navigation for exams
  • Updates: Lock mode changes propagate in < 100ms via WebSocket

Examples

Complete Lesson Setup

javascript
// Setup a complete lesson with 4 frames
async function createLesson(boardId, apiKey) {
  const frames = [
    { name: 'Introduction', lockMode: 'soft', bg: '#E3F2FD' },
    { name: 'Key Concepts', lockMode: 'soft', bg: '#FFF3E0' },
    { name: 'Practice', lockMode: 'soft', bg: '#F1F8E9' },
    { name: 'Summary', lockMode: 'none', bg: '#FCE4EC' }
  ];

  const created = [];
  for (let i = 0; i < frames.length; i++) {
    const frame = await fetch(
      `https://api.boardapi.io/api/v1/boards/${boardId}/frames`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': apiKey,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          name: frames[i].name,
          order: i,
          bounds_x: 1300 * i,
          bounds_y: 0,
          bounds_width: 1200,
          bounds_height: 800,
          lock_mode: frames[i].lockMode,
          background_color: frames[i].bg
        })
      }
    ).then(r => r.json());

    created.push(frame);
  }

  return created;
}

Breakout Room Workflow

javascript
// Complete breakout room setup with navigation
async function setupBreakoutRooms(boardId, templateFrameId, groups) {
  const rooms = [];

  for (let i = 0; i < groups.length; i++) {
    // Clone frame
    const room = await fetch(
      `/api/v1/boards/${boardId}/frames/${templateFrameId}/clone`,
      {
        method: 'POST',
        headers: {
          'X-API-Key': 'your-api-key',
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          offsetX: 1300 * (i + 1),
          offsetY: 0,
          name: groups[i].name
        })
      }
    ).then(r => r.json());

    rooms.push(room);

    // Navigate students to their rooms
    for (const studentSocket of groups[i].students) {
      studentSocket.emit('frame:navigate', {
        boardId: boardId,
        frameId: room.id,
        smooth: true
      });
    }
  }

  console.log(`Created ${rooms.length} breakout rooms`);
  return rooms;
}

// Usage:
const groups = [
  { name: 'Blue Team', students: [socket1, socket2] },
  { name: 'Red Team', students: [socket3, socket4] },
  { name: 'Green Team', students: [socket5, socket6] }
];

await setupBreakoutRooms('board-uuid', 'template-frame-id', groups);

Common Issues

Students not seeing frame navigation

Check:

  1. Students are connected to WebSocket
  2. frame:navigate event was broadcast (not direct message)
  3. Students' clients are listening to frame:navigated event
  4. Frame ID is valid and visible

Lock mode not working

Check:

  1. Frame lock_mode is set correctly (GET frame to verify)
  2. Client is enforcing lock (check viewport controller)
  3. Lock mode change was broadcast via WebSocket
  4. Students received frame:updated event

Cloned frames overlapping

Solution: Use consistent offset spacing:

javascript
const FRAME_SPACING = 1300; // Frame width (1200) + gap (100)
const offsetX = FRAME_SPACING * cloneIndex;

Frame performance degradation

Solutions:

  • Keep frame count under 50
  • Archive unused frames (DELETE endpoint)
  • Don't nest frames (not supported)
  • Use simple background colors (not images)

Quota & Limits

PlanMax Frames/BoardClone Operations/DayFrame Navigation Events/Day
Starter201001,000
Professional5050010,000
Business1002,00050,000
Enterprise500UnlimitedUnlimited

View Quota API →

Next Steps


Ready to use Frames? View Quick Start →