Skip to content

Whiteboard

This guide covers whiteboard initialization, toolbar operations, navigation, courseware management, and permission control.

Initialization

Pass board.domId in the constructor. The whiteboard initializes automatically when joining a classroom:

typescript
const classroom = new TencentClassroom({
  board: {
    domId: 'board-container',  // Whiteboard mount container DOM ID
    ratio: '16:9',             // Aspect ratio
    style: {
      brushColor: '#ff0000',
      brushThin: 100,
    },
  },
});

Manual Initialization

If board.domId was not set in the constructor, initialize manually after joining:

typescript
const result = await classroom.initBoard({
  domId: 'board-container',
  ratio: '16:9',
});
if (!result.ok) {
  console.error('Board initialization failed:', result.message);
}

Whiteboard Ready State

typescript
// Subscribe to board readiness
classroom.state.boardReady$.subscribe(ready => {
  if (ready) {
    console.log('Whiteboard is ready');
  }
});

// Listen for board ready event
classroom.on(TEvent.BOARD_READY, () => {
  console.log('Whiteboard history sync complete');
});

// Listen for board errors
classroom.on(TEvent.BOARD_ERROR, ({ code, message }) => {
  console.error(`Board error [${code}]:`, message);
});

Toolbar Operations

Set Active Tool

typescript
import { BoardToolType } from '@tencent-classroom/sdk';

classroom.setBoardToolType(BoardToolType.Pen);        // Freehand pen
classroom.setBoardToolType(BoardToolType.Mouse);      // Select/pointer
classroom.setBoardToolType(BoardToolType.Text);       // Text input
classroom.setBoardToolType(BoardToolType.Eraser);     // Eraser
classroom.setBoardToolType(BoardToolType.Laser);      // Laser pointer
classroom.setBoardToolType(BoardToolType.Line);       // Straight line
classroom.setBoardToolType(BoardToolType.Rect);       // Rectangle
classroom.setBoardToolType(BoardToolType.ZoomDrag);   // Pan and zoom

Brush Settings

typescript
// Set brush color
classroom.setBrushColor('#ff0000');

// Set brush thickness
classroom.setBrushThin(100);

// Enable/disable handwriting effect
classroom.setHandwritingEnable(true);

Text Settings

typescript
// Text style (0=normal, 1=bold, 2=italic, 3=bold+italic)
classroom.setTextStyle(1);

// Text size
classroom.setTextSize(24);

// Text color
classroom.setTextColor('#333333');

Drawing Permission

typescript
// Enable/disable drawing
classroom.setBoardDrawEnable(true);
classroom.setBoardDrawEnable(false);

Undo / Redo / Clear

typescript
classroom.undoBoard();   // Undo
classroom.redoBoard();   // Redo
classroom.clearBoard();  // Clear current page
typescript
// Previous / next page
classroom.prevBoard();
classroom.nextBoard();

// Jump to a specific page number
classroom.gotoBoardPage(3);

// Get current page and total pages
const currentPage = classroom.getBoardCurrentPage();
const totalPages = classroom.getBoardTotalPages();
console.log(`Page ${currentPage} / ${totalPages}`);

Zoom

typescript
// Get current scale factor
const scale = classroom.getBoardScale();

// Set scale factor
classroom.setBoardScale(150); // 150%

// Set zoom anchor point
classroom.setScaleAnchor(0.5, 0.5); // Center zoom

Content Fit Mode

typescript
import { BoardFitMode } from '@tencent-classroom/sdk';

classroom.setBoardContentFitMode(BoardFitMode.Contain);

Background

typescript
// Set background color
classroom.setBoardBackgroundColor('#f5f5f5');

// Set background image
classroom.setBoardBackgroundImage('https://example.com/bg.png');

Courseware Management

Upload Courseware

typescript
import { CoursewarePermission, TEvent } from '@tencent-classroom/sdk';

const result = await classroom.uploadCourseware({
  file: selectedFile,
  schoolId: 100,
  permission: CoursewarePermission.Private,
  docType: 'pptx',
  onReady: (taskId) => {
    console.log('Upload task ID:', taskId);
  },
  onProgress: (loaded, total, speed, percent) => {
    console.log(`Upload progress: ${Math.round(percent * 100)}%`);
  },
});

if (!result.ok) {
  console.error('Upload failed:', result.message);
  return;
}
console.log('Upload complete, docId:', result.data.docId);

Listen for Upload/Transcode Progress

typescript
classroom.on(TEvent.DOC_UPLOAD_PROGRESS, (progress) => {
  switch (progress.status) {
    case UploadTrackStatus.Uploading:
      setUploadBar(progress.filePercent);
      break;
    case UploadTrackStatus.Transcoding:
      setTranscodeBar(progress.transcodeProgress);
      break;
    case UploadTrackStatus.Completed:
      showToast('Courseware ready');
      break;
    case UploadTrackStatus.Failed:
      showError('Courseware processing failed');
      break;
  }
});

Load Courseware to Board

typescript
// Load courseware to whiteboard
classroom.loadCourseware(docInfo);

// Force reload (refresh at class start)
classroom.loadCourseware(docInfo, true);

Courseware List Management

typescript
// Get current classroom's courseware list (schoolId/classId filled automatically by SDK)
const listResult = await classroom.getClassCoursewares({
  page: 1,
  limit: 20,
});
if (listResult.ok) {
  console.log('Courseware list:', listResult.data.documents);
}

// Get school courseware library (for search/management scenarios)
const schoolDocs = await classroom.getSchoolCoursewares({
  page: 1,
  limit: 20,
  permission: [0, 1], // [0]=private [1]=public
  keyword: 'Chapter 1',
});

// Delete courseware
await classroom.deleteDocument('doc_001');

// Bind courseware to classroom
await classroom.bindDocumentToClass('doc_001');

// Unbind
await classroom.unbindDocumentFromClass('doc_001');

File Format Validation

typescript
// Validate file format and size before upload (sync, no network request)
const validateResult = classroom.validateUploadFile(file);
if (!validateResult.ok) {
  showToast(validateResult.message); // e.g. "Unsupported file format"
  return;
}

Board File Operations

typescript
// Add a transcoded file
const fileId = classroom.addBoardTranscodeFile(transcodeResult);

// Add image files
classroom.addBoardImagesFile(['url1.png', 'url2.png'], 'Image Collection');

// Add video file
classroom.addBoardVideoFile('https://example.com/video.mp4', 'Lecture Video');

// Switch to a file
classroom.switchBoardFile(fileId);

// Delete a file
classroom.deleteBoardFile(fileId);

// Get file list
const fileList = classroom.getBoardFileInfoList();

Whiteboard Permission Control

Role Capabilities

CapabilityTeacherAssistantStudentSupervisor
DrawingRequires grant
Toolbar toolsRequires grant
Page navigationRequires grant
Load courseware
View whiteboard
H5 PPT interaction (click video/popup)
  • Teacher/Assistant: Full whiteboard permission by default on join
  • Student: No whiteboard permission by default (view-only); teacher must grant permission for drawing and tools
  • Supervisor: View-only, no operations allowed

Permission Check

typescript
// Check whiteboard permission (drawing, tools, navigation — all board operations)
classroom.canOperateBoard(); // => boolean

// Subscribe to whiteboard permission changes
classroom.state.boardPermission$.subscribe((hasPermission) => {
  if (hasPermission) {
    showBoardToolbar();  // Show toolbar
  } else {
    hideBoardToolbar();  // Hide toolbar
  }
});

Unified Whiteboard Permission

The backend issues a single board permission field covering drawing, tool switching, page navigation, and all other whiteboard operations.

Dynamic Permission Grant / Revoke

Teachers/assistants can dynamically grant or revoke whiteboard permission for students via memberAction:

typescript
import { MemberActionType } from '@tencent-classroom/sdk';

// Grant whiteboard permission to a student
await classroom.memberAction({
  userId: 'student_001',
  action: MemberActionType.Board_Enable,
});

// Revoke whiteboard permission
await classroom.memberAction({
  userId: 'student_001',
  action: MemberActionType.Board_Disable,
});

After granting, the student's boardPermission$ automatically becomes true, and the SDK internally:

  • setDrawEnable(true) — enables drawing input
  • setDataSyncEnable(true) — syncs operations to other participants
  • Auto-switches tool to Pen

After revoking:

  • setDrawEnable(false) — disables drawing input
  • setDataSyncEnable(false) — operations are not synced
  • Auto-switches tool to MOUSE (H5 PPT interaction still preserved)

Permission and Class Status Coupling

  • Before class starts: Regardless of permission, board operations are not synced (setDataSyncEnable(false)), useful for pre-class prep drawing
  • During class: Operations are synced based on permission. The SDK automatically clears local pre-class operations at the moment class starts

More Board Events

In addition to BOARD_READY and BOARD_ERROR, the SDK provides the following board-related events:

typescript
import { TEvent } from '@tencent-classroom/sdk';

// Board DOM mounted, history sync in progress (board visible but not yet operable)
classroom.on(TEvent.BOARD_INIT, () => {
  showBoardLoading();
});

// Board signature/auth error (does not trigger retry)
classroom.on(TEvent.BOARD_SIG_ERROR, ({ code, message }) => {
  console.error('Board auth error:', code, message);
});

// Board warning (e.g., page limit exceeded, code=10)
classroom.on(TEvent.BOARD_WARN, ({ code, message }) => {
  console.warn(`Board warning [${code}]:`, message);
});

// Transcoded courseware loaded to board successfully
classroom.on(TEvent.BOARD_ADD_TRANSCODE_FILE, (fileId) => {
  console.log('Courseware loaded to board, fileId:', fileId);
});

// H5 PPT status changed
classroom.on(TEvent.BOARD_H5PPT_STATUS_CHANGED, (status, data) => {
  console.log('H5PPT status changed:', status);
});

// Embedded video status changed
classroom.on(TEvent.BOARD_VIDEO_STATUS_CHANGED, (data) => {
  console.log('Board video status:', data);
});

// H5PPT media playback status (teacher side for video sync)
classroom.on(TEvent.BOARD_MEDIA_STATUS_CHANGED, (fileId, mediaId, status, currentTime) => {
  // Sync playback progress from teacher to students
});

// Board image load status (1=error, 2=timeout)
classroom.on(TEvent.BOARD_IMAGE_STATUS_CHANGED, (status, data) => {
  if (status === 1) console.error('Board image failed to load');
});

// Courseware list updated
classroom.on(TEvent.DOC_LIST_UPDATED, ({ documents }) => {
  refreshCoursewarePanel(documents);
});

// Courseware being loaded to board
classroom.on(TEvent.DOC_LOAD_COURSEWARE, ({ type, docInfo }) => {
  console.log('Loading courseware:', docInfo.name);
});