# Managing Multiple Canvas Outputs with obs_canvas_t in OBS Studio

> Master multiple canvas outputs using the obs_canvas_t API in OBS Studio. Effortlessly manage independent rendering targets and dedicated video mixes for simultaneous outputs.

- Repository: [OBS Project/obs-studio](https://github.com/obsproject/obs-studio)
- Tags: how-to-guide
- Published: 2026-03-03

---

**The `obs_canvas_t` API in OBS Studio enables developers to create independent rendering targets with dedicated video mixes, allowing simultaneous output to multiple canvases from a single instance.**

Managing multiple canvas outputs with `obs_canvas_t` provides a powerful mechanism for plugins and modules in the **obsproject/obs-studio** repository to generate secondary video streams, preview feeds, or off-screen compositing targets. Each canvas maintains its own video mix, resolution, and frame rate, operating independently from the main program output while integrating seamlessly with the existing source and scene graph.

## Understanding the OBS Canvas Architecture

The canvas subsystem centers around the **`obs_canvas_t`** structure, a reference-counted object defined in [`libobs/obs.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.h) and implemented in [`libobs/obs-canvas.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-canvas.c). Unlike the single global canvas used for standard broadcasting, this API allows instantiation of multiple concurrent rendering contexts.

Each canvas encapsulates:
- A dedicated **`video_t`** structure for independent format configuration
- A private video mix for compositing sources
- Channel assignments (0-based indices) for source binding
- Reference counting for thread-safe lifecycle management

The architecture distinguishes between **public canvases** (visible in the UI, renameable) and **private canvases** (ephemeral, UI-invisible), controlled through separate constructor functions.

## Creating and Configuring Canvas Outputs

### Public vs Private Canvases

To create a canvas that integrates with the OBS UI and persists across sessions, use **`obs_canvas_create`**:

```c
struct obs_video_info vinfo = {
    .width = 1920,
    .height = 1080,
    .fps_num = 60,
    .fps_den = 1,
    .format = VIDEO_FORMAT_NV12
};

obs_canvas_t *canvas = obs_canvas_create(
    "SecondaryOutput",
    &vinfo,
    OBS_CANVAS_ACTIVATE | OBS_CANVAS_MIX_AUDIO
);

```

For hidden, temporary canvases suitable for plugins or background processing, **`obs_canvas_create_private`** in [`libobs/obs-canvas.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-canvas.c) (lines 200-204) allocates a canvas that bypasses the UI registry:

```c
obs_canvas_t *private_canvas = obs_canvas_create_private(
    "BackgroundCanvas", &vinfo, OBS_CANVAS_ACTIVATE
);

```

### Setting Canvas Properties

Canvas configuration occurs at creation time through the `obs_video_info` structure. Unlike the main output, each canvas can maintain distinct resolutions and frame rates, enabling scenarios like generating a 640x360 preview while broadcasting 1920x1080 main content.

## Attaching Sources and Scenes to Canvases

### Channel Assignment with obs_canvas_set_channel

Sources attach to canvases through **channel binding**, implemented in [`libobs/obs-canvas.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-canvas.c) (lines 429-475). The function **`obs_canvas_set_channel`** binds an `obs_source_t` to a specific channel index:

```c
obs_source_t *source = obs_get_source_by_name("Webcam");
if (source) {
    obs_canvas_set_channel(canvas, 0, source);  // Bind to channel 0
    obs_source_release(source);  // Release our reference; canvas holds its own
}

```

Channel assignment automatically manages source activation and deactivation lifecycle events. When the canvas becomes active, bound sources initialize; when deactivated, they release resources.

### Creating Dedicated Canvas Scenes

For complex compositions, **`obs_canvas_scene_create`** (lines 82-86 in [`libobs/obs-canvas.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-canvas.c)) creates a scene source specifically bound to the canvas:

```c
obs_scene_t *scene = obs_canvas_scene_create(canvas, "CanvasScene");
if (scene) {
    // Add sources to the scene as normal
    obs_sceneitem_t *item = obs_scene_add(scene, some_source);
}

```

This approach leverages the existing scene graph while redirecting output to the secondary canvas rather than the main program mix.

## Rendering and Output Management

### On-Demand Texture Rendering

Canvases render through **`obs_render_canvas_texture`**, defined in [`libobs/obs.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.c) (lines 32-36). This function draws the canvas's video mix to the current graphics context:

```c
void render_preview(void)
{
    // Called within a valid graphics context (e.g., filter draw callback)
    obs_render_canvas_texture(preview_canvas);
    
    // Access the underlying texture if needed for custom processing
    video_t *video = obs_canvas_get_video(preview_canvas);
    // video->texrender contains the rendered output
}

```

The color-only variant allows rendering without alpha blending for specific encoding scenarios.

### Canvas Enumeration and Cleanup

For lifecycle management, **`obs_enum_canvases`** (line 1954 in [`libobs/obs.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.c)) iterates all existing canvases:

```c
static bool canvas_callback(void *param, obs_canvas_t *canvas)
{
    const char *name = obs_canvas_get_name(canvas);
    blog(LOG_INFO, "Active canvas: %s", name);
    return true;  // Continue enumeration
}

void list_all_canvases(void)
{
    obs_enum_canvases(canvas_callback, NULL);
}

```

**Reference counting** ensures thread safety. Obtain strong references via `obs_canvas_get_ref` and release with `obs_canvas_release`. Weak references (`obs_weak_canvas_t`) allow safe cross-thread usage without preventing destruction.

To remove a canvas, call **`obs_canvas_remove`**, which marks the canvas as removed and releases internal references. Subsequent API calls on that canvas will fail safely.

## Practical Implementation Examples

The following complete example demonstrates creating a private preview canvas, attaching a scene, and rendering it:

```cpp
#include <obs-module.h>
#include <obs-scene.h>

static obs_canvas_t *preview_canvas = NULL;
static obs_scene_t *preview_scene = NULL;

bool init_preview_canvas(void)
{
    // Configure video parameters for 640x360 preview
    struct obs_video_info vinfo = {
        .width = 640,
        .height = 360,
        .fps_num = 30,
        .fps_den = 1,
        .format = VIDEO_FORMAT_NV12
    };
    
    // Create private canvas (invisible to UI)
    preview_canvas = obs_canvas_create_private("PreviewCanvas", &vinfo, 
                                               OBS_CANVAS_ACTIVATE);
    if (!preview_canvas) {
        blog(LOG_ERROR, "Failed to create preview canvas");
        return false;
    }
    
    // Create scene bound to this canvas
    preview_scene = obs_canvas_scene_create(preview_canvas, "PreviewScene");
    if (!preview_scene) {
        blog(LOG_ERROR, "Failed to create scene");
        obs_canvas_remove(preview_canvas);
        return false;
    }
    
    // Add main program view as source
    obs_source_t *program = obs_get_source_by_name("Program");
    if (program) {
        obs_sceneitem_t *item = obs_scene_add(preview_scene, program);
        obs_source_release(program);
        
        // Scale to fit canvas
        struct vec2 scale = {0.5f, 0.5f};
        obs_sceneitem_set_scale(item, &scale);
    }
    
    return true;
}

void render_preview_frame(void)
{
    if (preview_canvas) {
        obs_render_canvas_texture(preview_canvas);
    }
}

void cleanup_preview(void)
{
    if (preview_scene) {
        obs_scene_release(preview_scene);
        preview_scene = NULL;
    }
    if (preview_canvas) {
        obs_canvas_remove(preview_canvas);
        obs_canvas_release(preview_canvas);
        preview_canvas = NULL;
    }
}

```

## Summary

Managing multiple canvas outputs with `obs_canvas_t` enables sophisticated multi-streaming and compositing workflows in OBS Studio:

- **Independent rendering contexts** allow simultaneous outputs with different resolutions, frame rates, and formats from a single OBS instance.
- **Public and private canvas types** support both UI-integrated workflows and background processing tasks.
- **Channel-based source binding** via `obs_canvas_set_channel` integrates existing sources and scenes into secondary canvases without duplication.
- **Reference-counted lifecycle management** ensures thread-safe creation, enumeration, and destruction of canvas objects.
- **On-demand rendering** through `obs_render_canvas_texture` provides flexible output capture for encoders, filters, or external applications.

## Frequently Asked Questions

### What is the difference between obs_canvas_create and obs_canvas_create_private?

**`obs_canvas_create`** generates a public canvas registered in the UI canvas list, allowing users to rename and manage it through the OBS interface, while **`obs_canvas_create_private`** creates an ephemeral canvas invisible to the UI that cannot be renamed or persisted. Private canvases suit plugin background processing, while public canvases suit user-facing secondary outputs.

### How does reference counting work for obs_canvas_t objects?

OBS implements **strong and weak reference counting** for thread-safe canvas management. Obtain a strong reference with `obs_canvas_get_ref` to ensure the canvas persists while in use, and release it with `obs_canvas_release`. Weak references (`obs_weak_canvas_t`) allow checking canvas validity across threads without preventing destruction, converting to strong references only when needed.

### Can different canvases have different video formats and resolutions?

**Yes**, each canvas maintains an independent `video_t` structure configured during creation via the `obs_video_info` parameter. This allows one canvas to render at 1920x1080 NV12 for broadcast while another renders at 640x360 I420 for preview, with independent frame rates and color spaces, all processed within the same OBS instance.

### What is the proper cleanup sequence for a canvas?

**First**, remove all sources from the canvas using `obs_canvas_set_channel` with NULL for each channel. **Second**, release any scene references created with `obs_canvas_scene_create`. **Third**, call `obs_canvas_remove` to mark the canvas as destroyed. **Finally**, release your strong reference with `obs_canvas_release` to allow complete deallocation. This sequence prevents dangling references and ensures proper cleanup of the underlying video mix.