# Appliance plugin (/en/realtime-media/whiteboard/build/draw-and-edit-content/appliance-plugin)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

The Appliance Plugin is a high-performance whiteboard tool that enhances teaching aids with advanced annotation features. It includes laser pen support, customizable strokes and shapes, split-screen display, minimap navigation, element filtering, and searchable icon stickers.

This feature is for Web only.

This page describes how to integrate and use the Appliance Plugin in your app.

## Understand the tech [#understand-the-tech]

The plugin uses the 2D functionality of `SpriteJS`, supports `WebGL2` rendering, and remains compatible with `WebGL` and `Canvas2D` as fallback options. It employs a dual web `Worker` and `OffscreenCanvas` mechanism to handle drawing calculations and rendering in a separate worker thread, minimizing CPU usage on the main thread.

## Prerequisites [#prerequisites]

Before you begin, ensure that you have:

* Integrated the Fastboard SDK into your project.
* Implemented the logic to [join a whiteboard room](../set-up-and-build-your-first-app/get-started-uikit/#join-the-whiteboard-room).

## Integrate the plugin [#integrate-the-plugin]

Follow these steps to integrate the Appliance Plugin:

### Install the package [#install-the-package]

To install the plugin, open a terminal in your project folder and execute the following command:

```sh
npm install @netless/appliance-plugin
```

### Import the dependencies [#import-the-dependencies]

The plugin supports two modes:

* **Single whiteboard mode**

  ```js
  import { ApplianceSinglePlugin, ApplianceSingleWrapper } from '@netless/appliance-plugin';
  ```

* **Multi-window mode**

  ```js
  import { ApplianceMultiPlugin } from '@netless/appliance-plugin';
  ```

### Load the Appliance Plugin workers [#load-the-appliance-plugin-workers]

The Appliance Plugin requires Web Workers to handle specific tasks efficiently. You can load these workers in two ways:

1. Using a CDN (Recommended): No need to import local worker files.
2. Using local distribution (dist): Manually import the worker files and configure them properly.

The following code demonstrates how to load Web Workers from local distribution and integrate them with Fastboard:

```tsx
// Import worker scripts as raw strings from local distribution

// Convert to Blob URLs for worker usage
const fullWorkerBlob = new Blob([fullWorkerString], { type: 'text/javascript' });
const fullWorkerUrl = URL.createObjectURL(fullWorkerBlob);
const subWorkerBlob = new Blob([subWorkerString], { type: 'text/javascript' });
const subWorkerUrl = URL.createObjectURL(subWorkerBlob);
```

Once the workers are loaded, integrate them with Fastboard as follows:

* Fastboard React integration

  ```tsx
  import { useFastboard, Fastboard } from "@netless/fastboard-react";

  const app = useFastboard(() => ({
      sdkConfig: { ... },
      joinRoom: { ... },
      managerConfig: {
        cursor: true,
        enableAppliancePlugin: true,
        ...
      },
      enableAppliancePlugin: {
        cdn: {
            fullWorkerUrl,
            subWorkerUrl,
        }
      }
  }));
  ```

* Fastboard standalone integration

  ```tsx
  import { createFastboard, createUI } from "@netless/fastboard";

  const fastboard = await createFastboard({
      sdkConfig: { ... },
      joinRoom: { ... },
      managerConfig: {
        cursor: true,
        supportAppliancePlugin: true,
        ...
      },
      enableAppliancePlugin: {
        cdn: {
            fullWorkerUrl,
            subWorkerUrl,
        }
      }
  });
  ```

### Integrate the Appliance Plugin with Whiteboard and WindowManager [#integrate-the-appliance-plugin-with-whiteboard-and-windowmanager]

The Appliance Plugin extends the functionality of the Netless Whiteboard by enabling additional interactive tools. This section demonstrates how to:

* Set up `WhiteWebSdk` and join a room
* Mount `WindowManager` for multi-window support
* Enable the Appliance Plugin with Web Workers

1. Import required dependencies

   To use `WindowManager` and the Appliance Plugin, import the necessary styles and modules:

   ```tsx
   import '@netless/window-manager/dist/style.css';
   import '@netless/appliance-plugin/dist/style.css';

   import { WhiteWebSdk } from "white-web-sdk";
   import { WindowManager } from "@netless/window-manager";
   import { ApplianceMultiPlugin } from '@netless/appliance-plugin';
   ```

2. Load Web Workers for the Appliance Plugin

   If using local distribution (dist), manually import worker files and convert them into `Blob` URLs:

   ```tsx
   import fullWorkerString from '@netless/appliance-plugin/dist/fullWorker.js?raw';
   import subWorkerString from '@netless/appliance-plugin/dist/subWorker.js?raw';

   const fullWorkerBlob = new Blob([fullWorkerString], { type: 'text/javascript' });
   const fullWorkerUrl = URL.createObjectURL(fullWorkerBlob);
   const subWorkerBlob = new Blob([subWorkerString], { type: 'text/javascript' });
   const subWorkerUrl = URL.createObjectURL(subWorkerBlob);
   ```

3. Initialize `WhiteWebSdk` and join a room

   Create a `WhiteWebSdk` instance and join a whiteboard room:

   ```tsx
   const whiteWebSdk = new WhiteWebSdk(...);

   const room = await whiteWebSdk.joinRoom({
       ...
       invisiblePlugins: [WindowManager, ApplianceMultiPlugin],
       useMultiViews: true,
   });
   ```

4. Mount `WindowManager` and Enable the Appliance Plugin

   After joining the room, mount `WindowManager` and enable the `ApplianceMultiPlugin`:

   ```tsx
   const manager = await WindowManager.mount({
       room,
       container: elm,
       chessboard: true,
       cursor: true,
       supportAppliancePlugin: true
   });

   if (manager) {
       await ApplianceMultiPlugin.getInstance(manager, {
           options: {
               cdn: {
                   fullWorkerUrl,
                   subWorkerUrl,
               }
           }
       });
   }
   ```

### Using the Appliance Plugin with a single Whiteboard [#using-the-appliance-plugin-with-a-single-whiteboard]

The Appliance Plugin enhances the `WhiteWebSdk` by adding interactive tools for a single whiteboard instance. This section explains how to:

* Set up `WhiteWebSdk` and join a room
* Enable the Appliance Plugin for a single whiteboard
* Load Web Workers (if needed)

1. Import the required dependencies

   ```tsx
   import { WhiteWebSdk } from "white-web-sdk";
   import { ApplianceSinglePlugin, ApplianceSigleWrapper } from '@netless/appliance-plugin';
   ```

2. Load Web Workers for the Appliance Plugin

   If using local distribution (dist), manually import worker scripts and convert them into Blob URLs:

   ```tsx
   import fullWorkerString from '@netless/appliance-plugin/dist/fullWorker.js?raw';
   import subWorkerString from '@netless/appliance-plugin/dist/subWorker.js?raw';

   const fullWorkerBlob = new Blob([fullWorkerString], { type: 'text/javascript' });
   const fullWorkerUrl = URL.createObjectURL(fullWorkerBlob);
   const subWorkerBlob = new Blob([subWorkerString], { type: 'text/javascript' });
   const subWorkerUrl = URL.createObjectURL(subWorkerBlob);
   ```

   <CalloutContainer type="info" />

If using a CDN, you do not need to manually import worker scripts.
\:::

3. Initialize `WhiteWebSdk` and join a room

   Create a `WhiteWebSdk` instance and join a whiteboard room:

   ```tsx
   const whiteWebSdk = new WhiteWebSdk(...);

   const room = await whiteWebSdk.joinRoom({
       ...
       invisiblePlugins: [ApplianceSinglePlugin],
       wrappedComponents: [ApplianceSigleWrapper]
   });
   ```

4. Enable the Appliance Plugin

   After joining a room, initialize the `ApplianceSinglePlugin`:

   ```tsx
   await ApplianceSinglePlugin.getInstance(room, {
       options: {
           cdn: {
               fullWorkerUrl,
               subWorkerUrl,
           }
       }
   });
   ```

## Implement features [#implement-features]

This section describes how to use various features of the Appliance Plugin.

### Use the laser pen feature [#use-the-laser-pen-feature]

The laser pen feature allows users to annotate with a pencil tool and draw temporary content that disappears after a second.

![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-laser-pen.gif)

To implement this feature:

1. **Import the required dependencies**:

   ```js
   import { EStrokeType, ApplianceNames } from '@netless/appliance-plugin';
   ```

2. **Set stroke type**:

   ```js
   // Solid line
   setMemberState({strokeType: EStrokeType.Normal });

   // Line with pen edge
   setMemberState({strokeType: EStrokeType.Stroke });

   // Dotted line
   setMemberState({strokeType: EStrokeType.Dotted });

   // Long dotted line
   setMemberState({strokeType: EStrokeType.LongDotted });
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-stroke-type.png)

3. **Set stroke opacity**:

   ```js
   setMemberState({strokeOpacity: 0.5 });
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-stroke-opacity.png)

4. **Set the text properties**:

   ```js
   setMemberState({textOpacity: 0.5, textBgOpacity: 0.5, textBgColor:[0, 0, 0]});
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-text-properties.png)

### Use different shapes [#use-different-shapes]

1. **Polygons**:

   ```js
   // Regular pentagon
   setMemberState({currentApplianceName: ApplianceNames.shape, shapeType: ShapeType.Polygon, vertices: 5});
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-shape-polygon.png)

2. **Stars**:

   ```js
   // Fat hexagonal star
   setMemberState({currentApplianceName: ApplianceNames.shape, shapeType: ShapeType.Star, vertices: 12, innerVerticeStep: 2, innerRatio: 0.8});
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-shape-star.png)

3. **Speech Balloon**:

   ```js
   // The dialog box in the lower left corner
   setMemberState({currentApplianceName: ApplianceNames.shape, shapeType: ShapeType.SpeechBalloon, placement: 'bottomLeft'});
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-shape-balloon.png)

4. **Set shape fill color and fill opacity**:

   ```js
   setMemberState({fillOpacity: 0.5, fillColor:[0, 0, 0]});
   ```

   ![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-shape-fill.png)

### Split the display screen [#split-the-display-screen]

To implement a split screen display, integrate the [`@netless/app-little-white-board`](https://github.com/netless-io/app-little-white-board) (Version >=1.1.3).

![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-split-screen.gif)

### Use the mini map feature [#use-the-mini-map-feature]

The mini-map feature displays a certain part of the whiteboard at a different zoom level than the main map control, allowing easier navigation.

```js
// Version >=1.1.6
/** Create a minimap
 * @param viewId ID of the whiteboard under windowManager. The ID of the main whiteboard is mainView, and the ID of other whiteboards is the appID of addApp() return
 * @param div Small map DOM container
 */
createMiniMap(viewId: string, div: HTMLElement): Promise<void>;
/** Destroy minimap */
destroyMiniMap(viewId: string): Promise<void>;
```

![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-minimap.gif)

### Filter annotations [#filter-annotations]

This feature allows you to control the visibility of annotations on a whiteboard for different users.

```js
/** Filter Elements
 * @param viewId ID of the whiteboard under windowManager. The ID of the main whiteboard is mainView, and the ID of other whiteboards is the appID of addApp() return
 * @param filter filter condition
 *  render: Whether notes can be rendered, [uid1, uid2,...] Or true. true, that is, both render, [uid1, uid2,...] The collection of user Uids rendered for the specified
 *  hide: Note is hidden, [uid1, uid2,...] Or true. true, that is to hide, [uid1, uid2,...] To specify a hidden user uid collection
 *  clear: Whether notes can be cleared, [uid1, uid2,...] Or true. true, that is, can be cleared, [uid1, uid2,...] Specifies a collection of user Uids that can be cleared
 * @param isSync Whether to synchronize data to other users. The default value is true, that is, the data will be synchronized to other users
 */
filterRenderByUid(viewId: string, filter: { render?: _ArrayTrue, hide?: _ArrayTrue, clear?: _ArrayTrue}, isSync?:boolean): void;
/** Filter Elements
 * @param viewId ID of the whiteboard under windowManager. The ID of the main whiteboard is mainView, and the ID of other whiteboards is the appID of addApp() return
 * @param isSync Whether to synchronize data to other users. The default value is true, that is, the data will be synchronized to other users. Keep it the same as the filterRenderByUid setting
 */
cancelFilterRender(viewId: string, isSync?:boolean): void;
```

![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-filter.gif)

### Use the auto draw feature [#use-the-auto-draw-feature]

The auto draw feature is designed to automatically recognize and suggest related graphical elements based on a user's hand-drawn sketches on a whiteboard.

```js
export type AutoDrawOptions = {
    /** Automatically associate rest api addresses */
    hostServer: string;
    /** A container that holds a list of associated icons */
    container: HTMLDivElement;
    /** How long does the drawing end start activating the association */
    delay?: number;
};
const plugin = await ApplianceMultiPlugin.getInstance(...);
const autoDrawPlugin = new AutoDrawPlugin({
    container: topBarDiv,
    hostServer: 'https://autodraw-white-backup-hk-hkxykbfofr.cn-hongkong.fcapp.run',
    delay: 2000
});
plugin.usePlugin(autoDrawPlugin);
```

![](https://assets-docs.agora.io/images/interactive-whiteboard/appliance-autodraw.gif)

## Reference [#reference]

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

### API References [#api-references]

* `cleanCurrentScene`: Clears the current whiteboard scene.
* `setMemberState`: Updates the whiteboard member's state, such as stroke type and color.
* `undo`: Reverts the last action performed on the whiteboard.
* `redo`: Restores the last undone action on the whiteboard.
* `callbacks`: Registers event callbacks for appliance-plugin interactions.
* `insertImage`: Inserts an image into the whiteboard.
* `lockImage`: Locks an inserted image to prevent modifications.
* `completeImageUpload`: Finalizes the image upload process to the whiteboard.
* `getImagesInformation`: Retrieves metadata about images added to the whiteboard.
* `destroy`: Destroys the instance of the appliance-plugin.
* `addListener`: Adds an event listener to the appliance-plugin.
* `removeListener`: Removes an event listener from the appliance-plugin.

#### Deprecated [#deprecated]

The following APIs have been renamed or changed:

* `room.getBoundingRect`: Use `getBoundingRectAsync` instead.
* `room.screenshotToCanvas`: Use `screenshotToCanvasAsync` instead.
* `room.scenePreview`: Use `scenePreviewAsync` instead.

#### Incompatible [#incompatible]

With the appliance Plugin, these APIs are incompatible:

* `exportScene`: When the appliance-plugin is enabled, notes cannot be exported in room mode.
* `Server-side screenshot`: After enabling the appliance-plugin, notes cannot be obtained using the server-side screenshot API. Use `screenshotToCanvasAsync` instead.

### Webpack configuration for `?raw` imports [#webpack-configuration-for-raw-imports]

The Appliance Plugin requires loading Web Worker scripts. If you're using Vite, it supports `?raw` imports out of the box. However, Webpack needs additional configuration to handle `?raw` as a raw text asset. If your project uses Webpack, add the following rule to your `webpack.config.js` file:

```js
module.exports = {
    module: {
        rules: [
            // Default rule for JavaScript files
            {
                test: /\.m?js$/,
                resourceQuery: { not: [/raw/] }, // Exclude '?raw' imports
                use: [ ... ] // Your existing loaders
            },
            // Custom rule for handling '?raw' imports
            {
                resourceQuery: /raw/,
                type: 'asset/source', // Treats it as a raw text asset
            }
        ]
    }
};
```

### Configuration parameters [#configuration-parameters]

```js
getInstance(wm: WindowManager, adaptor: ApplianceAdaptor)
```

* `wm`: WindowManager\room\player. In multi-window mode, you pass WindowManager, and in single-window mode, you pass room or player(whiteboard playback mode).
* `adaptor`: configures the adapter.
  * `options: AppliancePluginOptions;` The cdn addresses of both workers must be configured.

    ```tsx
    export type AppliancePluginOptions = {
        /** cdn Configuration item */
        cdn: CdnOpt;
        /** Synchronize data configuration items */
        syncOpt? : SyncOpt;
        /** Canvas configuration item */
        canvasOpt? : CanvasOpt;
        /** stroke width range */
        strokeWidth?: {
            min: number,
            max: number,
        }
    }
    ```

    * `cursorAdapter?: CursorAdapter;` This parameter is optional. In single whiteboard mode, customize the mouse style.
    * `logger?: Logger;` This parameter is optional. Configure the log printer object. The default output is on the local console. To uploaded the log to the specified server, manually set the configuration.

### Front-end debugging [#front-end-debugging]

To understand and track the internal status of the plug-in during the interconnection process, you can view the internal data using the following console commands.

```tsx
const applianPlugin = await ApplianceSinglePlugin.getInstance(...)
applianPlugin.CurrentManager  // can see the package version number, internal state, etc
applianPlugin.CurrentManager.ConsoleWorkerInfo () // can check information to draw on the worker
```

### See also [#see-also]

* For further details, see [appliance-plugin](https://github.com/netless-io/fastboard/blob/main/docs/en/appliance-plugin.md).
