Recording

Screen Recording

Record screen activity with configurable FPS, buffer mode, and cursor visibility.

Overview

The @nut-tree/plugin-screenrecording plugin adds screen recording capabilities to nut.js. Record automation sessions for debugging or use buffer mode to capture only the last N seconds before a failure.

Recording

Record screen to video files

screen.startRecording(options)

Buffer Mode

Keep the last N seconds in memory

bufferSeconds: 30

Discard

Discard buffered recordings on success

screen.discardRecording()

Installation

typescript
npm install @nut-tree/plugin-screenrecording

Subscription Required

This package is included in Solo and Team subscription plans.

FFmpeg Required

This plugin requires FFmpeg to be installed and available on your system PATH.

Quick Reference

useScreenRecorder

useScreenRecorder()
void

Activate the screen recording plugin

screen.startRecording

screen.startRecording(options?: RecordingOptions)
Promise<void>

Start recording the screen

screen.stopRecording

screen.stopRecording(options?: StopRecordingOptions)
Promise<void>

Stop the current recording and save the video file

screen.discardRecording

screen.discardRecording()
Promise<void>

Discard the current recording without saving


Recording

Basic Recording

Record the screen to a video file:

typescript
import { screen } from "@nut-tree/nut-js";
import { useScreenRecorder } from "@nut-tree/plugin-screenrecording";

useScreenRecorder();

// Start recording
await screen.startRecording({ fps: 30, showCursor: true });

// ... perform automation ...

// Stop recording and save to file
await screen.stopRecording({ outputPath: "./recording.mp4" });

Recording with Options

typescript
import { screen } from "@nut-tree/nut-js";
import type { RecordingOptions } from "@nut-tree/plugin-screenrecording";
import { useScreenRecorder } from "@nut-tree/plugin-screenrecording";

useScreenRecorder();

const options: RecordingOptions = {
    fps: 30,
    showCursor: true,
    bufferSeconds: 60,
};

await screen.startRecording(options);

// ... perform automation ...

await screen.stopRecording({ outputPath: "./test-recording.mp4" });

Buffer Mode

Buffer mode keeps the last N seconds of screen activity in memory. When the recording is stopped, only the buffered portion is written to disk. If the test succeeds, you can discard the buffer entirely with screen.discardRecording():

typescript
import { screen } from "@nut-tree/nut-js";
import { useScreenRecorder } from "@nut-tree/plugin-screenrecording";

useScreenRecorder();

// Start recording with a 30-second buffer
await screen.startRecording({
    bufferSeconds: 30,
    fps: 30,
    showCursor: true,
});

try {
    // ... perform automation ...

    // Test passed — discard the recording
    await screen.discardRecording();
} catch (error) {
    // Test failed — save the last 30 seconds
    await screen.stopRecording({ outputPath: "./failure-capture.mp4" });
    throw error;
}

CI/CD Use Case

Buffer mode is particularly useful in CI/CD pipelines where you only want to keep recordings of failed test runs. Start the buffer at the beginning of each test and only save the recording if the test fails.

Test Framework Integration

Vitest Error Videos

Capture error videos automatically for failing tests using Vitest lifecycle hooks. The buffer records continuously and is either saved on failure or discarded on success:

typescript
import { afterEach, beforeAll, beforeEach, describe, it } from "vitest";
import { down, left, mouse, right, screen, up } from "@nut-tree/nut-js";
import type { RecordingOptions } from "@nut-tree/plugin-screenrecording";
import { useScreenRecorder } from "@nut-tree/plugin-screenrecording";

const recordingOptions: RecordingOptions = {
    bufferSeconds: 30,
    fps: 30,
    showCursor: true,
};

beforeAll(() => {
    useScreenRecorder();
});

beforeEach(async () => {
    await screen.startRecording(recordingOptions);
});

afterEach(async (context) => {
    const failed = context.task.result?.state === "fail";

    if (failed) {
        const safeName = context.task.name.replace(/[^a-zA-Z0-9]+/g, "_");
        await screen.stopRecording({
            outputPath: `./${safeName}_error_recording.mp4`,
        });
    } else {
        await screen.discardRecording();
    }
});

describe("Screen recording", () => {
    it("should move the mouse in a square", async () => {
        await mouse.move(right(500));
        await mouse.move(down(500));
        await mouse.move(left(500));
        await mouse.move(up(500));
    }, 30_000);

    it.fails("should detect a simulated failure", async () => {
        await mouse.move(right(200));
        await mouse.move(down(200));

        throw new Error("Simulated failure");
    }, 30_000);
});

Options Reference

RecordingOptions

fps

fps?: number
optional

Frames per second for the recording. Higher values produce smoother video but larger files.

showCursor

showCursor?: boolean
optional

Whether to show the cursor in the recording.

bufferSeconds

bufferSeconds?: number
optional

Enable buffer mode and keep only the last N seconds. Use with discardRecording() to discard on success.

StopRecordingOptions

outputPath

outputPath?: string
optional

File path for the output video. If not set, a default path is generated.

Best Practices

Recording Tips

  • Use buffer mode in CI/CD to capture failures without filling up disk space
  • Call discardRecording() in your test's afterEach on success to avoid accumulating recordings
  • Sanitize test names for file paths when using dynamic output names
  • Enable showCursor: true for debugging to see where clicks happen

FFmpeg

Make sure FFmpeg is installed and on your PATH. On macOS: brew install ffmpeg. On Ubuntu/Debian: apt-get install ffmpeg. On Windows: download from ffmpeg.org and add to PATH.

Was this page helpful?