# RCWeb screen examples

Version: 3

Published location: https://rcweb.live/mcp/screen-examples.md
Read the operating guide first: https://rcweb.live/mcp/llm-guide.md

These are starting points for a default viewer that the user wants to use for new display content. Inspect the selected client first. Do not cover an active app with these examples unless the user wants its display replaced. Send code only through `execute_javascript` to the exact selected client.

## Self-contained display recipes

Each code block below is a complete execution body. Send the whole block in ONE tool call. Each display recipe includes its own stage setup; no helper function or earlier setup call is required. Local variables and functions do not survive between executions. All names starting with `rcweb-assistant` or `__rcwebAssistant` below are owned by these examples, not built-in RCWeb APIs.

This overlay leaves the viewer runtime intact. It may cover its welcome controls. To restore them, run the cleanup function and remove only `rcweb-assistant-stage`. Inspect its dimensions and stacking on the real screen; do not assume that every app has the same layout.

## Display an image

Replace `USER_IMAGE_URL` with the user's approved direct image URL using safe JavaScript string serialization. The URL placeholder is not a real media address. This example fits an image without cropping and reports only after loading. Use an appropriate MCP timeout for the network; after a timeout inspect the image before retrying.

```javascript
if (typeof window.__rcwebAssistantCleanup === 'function') {
    window.__rcwebAssistantCleanup();
}
window.__rcwebAssistantCleanup = null;
var stage = document.getElementById('rcweb-assistant-stage');
if (!stage) {
    stage = document.createElement('div');
    stage.id = 'rcweb-assistant-stage';
    document.body.appendChild(stage);
}
while (stage.firstChild) stage.removeChild(stage.firstChild);
stage.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;background:#10253b;color:white;overflow:hidden;';
var image = document.createElement('img');
image.alt = 'Image selected by the user';
image.style.cssText = 'position:absolute;top:50%;left:50%;max-width:100%;max-height:100%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);';
image.onload = function () {
    reply({loaded: true, width: image.naturalWidth, height: image.naturalHeight});
};
image.onerror = function () { fail('The image could not be loaded. Check the URL and format.'); };
window.__rcwebAssistantCleanup = function () {
    image.onload = image.onerror = null;
    image.removeAttribute('src');
};
stage.appendChild(image);
image.src = 'USER_IMAGE_URL';
```

## Play a video

Replace `USER_VIDEO_URL` with an approved direct video URL, not a sharing webpage. This example requests muted autoplay and keeps controls available. It does not force fullscreen or audio playback. Its reply describes the state when media data arrives; if `playing` is false, inspect again or explain that a click on the display may be needed.

```javascript
if (typeof window.__rcwebAssistantCleanup === 'function') {
    window.__rcwebAssistantCleanup();
}
window.__rcwebAssistantCleanup = null;
var stage = document.getElementById('rcweb-assistant-stage');
if (!stage) {
    stage = document.createElement('div');
    stage.id = 'rcweb-assistant-stage';
    document.body.appendChild(stage);
}
while (stage.firstChild) stage.removeChild(stage.firstChild);
stage.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;background:#10253b;color:white;overflow:hidden;';
var video = document.createElement('video');
video.controls = true;
video.muted = true;
video.autoplay = true;
video.setAttribute('playsinline', '');
video.style.cssText = 'display:block;width:100%;height:100%;background:black;';
video.onloadeddata = function () {
    reply({loaded: true, playing: !video.paused, muted: video.muted, controls: video.controls});
};
video.onerror = function () {
    fail('Video could not be loaded. Media error code: ' + (video.error ? video.error.code : 'unknown'));
};
window.__rcwebAssistantCleanup = function () {
    video.onloadeddata = video.onerror = null;
    video.pause();
    video.removeAttribute('src');
    video.load();
};
stage.appendChild(video);
video.src = 'USER_VIDEO_URL';
```

For a later verification, inspect this stage's video: `readyState`, `paused`, `currentTime`, `videoWidth`, `videoHeight`, and `error`. Two observations with increasing `currentTime` are stronger evidence of playback than the initial load event. Report muted playback accurately.

## Create an analog clock

This example uses a canvas, no images or libraries, and the display browser's local time. Its timer is replaced rather than duplicated on the next recipe. If `getContext` is unavailable, report that limitation instead of claiming a working clock.

```javascript
if (typeof window.__rcwebAssistantCleanup === 'function') {
    window.__rcwebAssistantCleanup();
}
window.__rcwebAssistantCleanup = null;
var stage = document.getElementById('rcweb-assistant-stage');
if (!stage) {
    stage = document.createElement('div');
    stage.id = 'rcweb-assistant-stage';
    document.body.appendChild(stage);
}
while (stage.firstChild) stage.removeChild(stage.firstChild);
stage.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:1000;background:#10253b;color:white;overflow:hidden;';
var canvas = document.createElement('canvas');
canvas.width = canvas.height = 600;
canvas.style.cssText = 'position:absolute;top:50%;left:50%;width:80vmin;height:80vmin;max-width:100%;max-height:100%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);';
canvas.setAttribute('role', 'img');
var context = canvas.getContext && canvas.getContext('2d');
if (!context) { fail('This browser cannot draw a canvas clock.'); return; }
stage.appendChild(canvas);
function drawClock() {
    var now = new Date();
    canvas.setAttribute('aria-label', 'Local time: ' + now.toLocaleTimeString());
    context.clearRect(0, 0, 600, 600);
    context.save();
    context.translate(300, 300);
    context.beginPath();
    context.arc(0, 0, 280, 0, Math.PI * 2);
    context.fillStyle = '#ffffff';
    context.fill();
    context.strokeStyle = '#1769b5';
    var i;
    for (i = 0; i < 60; i++) {
        context.save();
        context.rotate(i * Math.PI / 30);
        context.lineWidth = i % 5 === 0 ? 6 : 2;
        context.beginPath();
        context.moveTo(0, -260);
        context.lineTo(0, i % 5 === 0 ? -235 : -250);
        context.stroke();
        context.restore();
    }
    function hand(angle, length, width, color) {
        context.save();
        context.rotate(angle);
        context.beginPath();
        context.moveTo(0, 12);
        context.lineTo(0, -length);
        context.lineWidth = width;
        context.lineCap = 'round';
        context.strokeStyle = color;
        context.stroke();
        context.restore();
    }
    hand(((now.getHours() % 12) + now.getMinutes() / 60) * Math.PI / 6, 145, 12, '#253b53');
    hand((now.getMinutes() + now.getSeconds() / 60) * Math.PI / 30, 210, 8, '#253b53');
    hand(now.getSeconds() * Math.PI / 30, 225, 3, '#1e87f0');
    context.restore();
}
drawClock();
var clockTimer = window.setInterval(drawClock, 1000);
window.__rcwebAssistantCleanup = function () { window.clearInterval(clockTimer); };
reply({created: true, kind: 'analog clock', time: new Date().toString(), width: canvas.width, height: canvas.height});
```

For subsequent color or size changes, adjust the existing clock implementation and replace its timer cleanly. Verify the stage is visible and the clock continues updating. The initial reply confirms drawing/setup, not observation of an elapsed second.

## Screenshot of the current screen

User example: “Take a screenshot of my screen and show it here.”

Do not change the display or run any display recipe setup above. Discover the target, then send this body through `execute_javascript` with `timeout_ms: 30000`. Replace `ROOM_ID` and `CLIENT_ID` with the selected room and exact discovered client ID. The helper returns metadata directly to the tool; the chat page downloads and displays the image automatically. It does not send the pixels to the LLM.

```javascript
var target = {room: 'ROOM_ID', client: 'CLIENT_ID'};
if (!window.rc || !rc.connected || rc.room !== target.room || rc.client !== target.client) {
    fail('The selected screen is no longer connected.');
} else if (window.__rcMcpCapture1 && window.__rcMcpCapture1.version === 2) {
    window.__rcMcpCapture1.capture(target, reply, fail);
} else {
    var script = document.createElement('script');
    var finished = false;
    var timer;
    var loaded = function (ok) {
        if (finished) return;
        finished = true;
        window.clearTimeout(timer);
        script.onload = script.onerror = null;
        if (script.parentNode) script.parentNode.removeChild(script);
        try {
            if (ok && window.__rcMcpCapture1 && window.__rcMcpCapture1.version === 2) {
                window.__rcMcpCapture1.capture(target, reply, fail);
            } else fail('The screenshot helper could not load on this screen.');
        } catch (error) { fail(String(error.message || error)); }
    };
    timer = window.setTimeout(function () { loaded(false); }, 8000);
    script.src = '/mcp/capture-bundle.js?v=2';
    script.onload = function () { loaded(true); };
    script.onerror = function () { loaded(false); };
    document.head.appendChild(script);
}
```

Do not rewrite the returned `rcweb-screenshot` object or make up a download URL. Mention any warnings returned by the helper. If capture fails, report the reason without claiming that a screenshot is available. If the user asks what is in the image, explain that this flow displays it to them but does not give the model image input.
