> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gravixlayer.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Display

> The virtual screen — enable it, capture it, and drive raw input

The display is a virtual screen inside the runtime. Everything visible in the
sandbox renders on it — headed browser windows, native dialogs, terminal apps,
canvas and WebGL output. You interact with it the way a human would: look at
the pixels, move the mouse, press keys.

Use the display plane for anything the browser's DOM cannot describe, and for
watching work happen live. CLI and cURL examples assume `$RT` holds a
`browser-small` runtime id (see [Overview](/documentation/agentruntime/browser-use/overview#requirements)).

## Endpoints

| Method | Path | Description |
| - | - | - |
| POST | `/v1/agents/runtime/{runtime_id}/display/enable` | Start the display server |
| POST | `/v1/agents/runtime/{runtime_id}/display/disable` | Stop it |
| GET | `/v1/agents/runtime/{runtime_id}/display/status` | Geometry and state |
| POST | `/v1/agents/runtime/{runtime_id}/display/screenshot` | Capture the screen (base64) |
| POST | `/v1/agents/runtime/{runtime_id}/display/wait_stable` | Block until pixels stop changing |
| POST | `/v1/agents/runtime/{runtime_id}/display/mouse/click` | Click at x,y |
| POST | `/v1/agents/runtime/{runtime_id}/display/mouse/move` | Move the pointer |
| POST | `/v1/agents/runtime/{runtime_id}/display/mouse/scroll` | Scroll the wheel |
| POST | `/v1/agents/runtime/{runtime_id}/display/keyboard/type` | Type a string |
| POST | `/v1/agents/runtime/{runtime_id}/display/keyboard/press` | Press one key (`Enter`, `Tab`, `F5`…) |
| POST | `/v1/agents/runtime/{runtime_id}/display/keyboard/hotkey` | Press a chord (`ctrl`, `l`) |
| POST | `/v1/agents/runtime/{runtime_id}/display/view` | Mint a live-watch URL |

## Enable and status

`enable` starts the display server and returns its status; calling it again
returns the running display's status unchanged, so it is safe to call first in
every script. Enable the display **before** `browser.open` to get a headed
browser window. The sections below assume the display is enabled.

```python Python theme={"dark"}
from gravixlayer import GravixLayer

client = GravixLayer()  # reads GRAVIXLAYER_API_KEY
sandbox = client.runtime.create(template="browser-small")

st = sandbox.display.enable(width=1920, height=1080)
print(st["enabled"], st["display"])  # True :0

print(sandbox.display.status())
sandbox.kill()
```

```typescript TypeScript theme={"dark"}
import { GravixLayer } from 'gravixlayer';

const client = new GravixLayer(); // reads GRAVIXLAYER_API_KEY
const sandbox = await client.runtime.create({ template: 'browser-small' });

const st = await sandbox.display.enable({ width: 1920, height: 1080 });
console.log(st.enabled, st.display); // true :0

console.log(await sandbox.display.status());
await sandbox.kill();
```

```bash CLI theme={"dark"}
gravixlayer runtime display enable "$RT" --width 1920 --height 1080
gravixlayer runtime display status "$RT"
```

```bash cURL theme={"dark"}
API="https://api.gravixlayer.ai/v1"

curl -sS -X POST "$API/agents/runtime/$RT/display/enable" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"width":1920,"height":1080}'

curl -sS "$API/agents/runtime/$RT/display/status" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
```

**Enable parameters**

| Parameter | Type | Required | Description |
| - | - | - | - |
| `width` | int | No | Framebuffer width — `1440` when omitted (the Python SDK and CLI send `1920`) |
| `height` | int | No | Framebuffer height — `900` when omitted (the Python SDK and CLI send `1080`) |
| `depth` | int | No | Color depth: `8`, `15`, `16`, `24` (default), `30`, or `32` |
| `display` | string | No | X11 display name (default `:0`) |
| `wm` | string | No | Window manager to start (default none) |

## Screenshot and wait\_stable

`screenshot` returns the frame as base64 PNG or JPEG — crop with `region`,
shrink with `scale_pct`, or pass `wait_stable_ms` to capture only after the
screen stops changing. `wait_stable` is the standalone version: block until
the screen has been pixel-quiet for `stable_ms`.

```python Python theme={"dark"}
import base64
from gravixlayer import GravixLayer

client = GravixLayer()
sandbox = client.runtime.create(template="browser-small")
sandbox.display.enable()

# Capture after the screen settles — the anti-flake pattern.
shot = sandbox.display.screenshot(wait_stable_ms=500)
with open("screen.png", "wb") as f:
    f.write(base64.b64decode(shot["data"]))

print(sandbox.display.wait_stable(stable_ms=300, timeout_ms=10_000))
sandbox.kill()
```

```typescript TypeScript theme={"dark"}
import { writeFileSync } from 'node:fs';
import { GravixLayer } from 'gravixlayer';

const client = new GravixLayer();
const sandbox = await client.runtime.create({ template: 'browser-small' });
await sandbox.display.enable();

// Capture after the screen settles — the anti-flake pattern.
const shot = await sandbox.display.screenshot({ waitStableMs: 500 });
writeFileSync('screen.png', Buffer.from(shot.data, 'base64'));

console.log(await sandbox.display.waitStable(300, 10_000));
await sandbox.kill();
```

```bash CLI theme={"dark"}
gravixlayer runtime display screenshot "$RT" --out screen.png --wait-stable-ms 500
gravixlayer runtime display wait-stable "$RT" --stable-ms 300 --timeout-ms 10000
```

```bash cURL theme={"dark"}
API="https://api.gravixlayer.ai/v1"

curl -sS -X POST "$API/agents/runtime/$RT/display/screenshot" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"format":"png","wait_stable_ms":500}' | jq -r .data | base64 -d > screen.png

curl -sS -X POST "$API/agents/runtime/$RT/display/wait_stable" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"stable_ms":300,"timeout_ms":10000}'
```

**Screenshot parameters**

| Parameter | Type | Required | Description |
| - | - | - | - |
| `format` | string | No | `png` (default) or `jpeg` |
| `region` | object | No | Crop `{x, y, w, h}` in screen pixels |
| `quality` | int | No | JPEG quality 1–100 |
| `scale_pct` | int | No | Downscale percentage |
| `wait_stable_ms` | int | No | Require pixel-quiet this long first |
| `timeout_ms` | int | No | Give up waiting after this (default 5000 when `wait_stable_ms` is set) |

Screenshot returns `{data, format, width, height}` with `data` base64.
`wait_stable` returns `{settled, waited_ms}` (`waitedMs` in TypeScript).

## Mouse and keyboard

Raw input at screen coordinates. `click` takes an optional `button`
(`left`/`middle`/`right`); `scroll` sends `dx`/`dy` wheel deltas at the
current pointer position — `move` first to aim it. `type` sends a string,
`press` one key name, and `hotkey` a chord.

```python Python theme={"dark"}
from gravixlayer import GravixLayer

client = GravixLayer()
sandbox = client.runtime.create(template="browser-small")
sandbox.display.enable()

sandbox.display.click(640, 420)                    # left click at x,y
sandbox.display.click(640, 420, button="right")
sandbox.display.move(320, 240)
sandbox.display.scroll(dy=-480)                    # wheel delta (dx/dy)
sandbox.display.type("user@example.com")
sandbox.display.press("Enter")
sandbox.display.hotkey(["ctrl", "l"])              # focus the URL bar
sandbox.kill()
```

```typescript TypeScript theme={"dark"}
import { GravixLayer } from 'gravixlayer';

const client = new GravixLayer();
const sandbox = await client.runtime.create({ template: 'browser-small' });
await sandbox.display.enable();

await sandbox.display.click(640, 420);             // left click at x,y
await sandbox.display.click(640, 420, 'right');
await sandbox.display.move(320, 240);
await sandbox.display.scroll(0, -480);             // wheel delta (dx, dy)
await sandbox.display.type('user@example.com');
await sandbox.display.press('Enter');
await sandbox.display.hotkey(['ctrl', 'l']);       // focus the URL bar
await sandbox.kill();
```

```bash CLI theme={"dark"}
gravixlayer runtime display mouse "$RT" click --x 640 --y 420 --button left
gravixlayer runtime display mouse "$RT" move --x 320 --y 240
gravixlayer runtime display mouse "$RT" scroll --dy=-480      # use = for negative values
gravixlayer runtime display keyboard "$RT" type --value "user@example.com"
gravixlayer runtime display keyboard "$RT" press --value Enter
gravixlayer runtime display keyboard "$RT" hotkey --keys ctrl,l
```

```bash cURL theme={"dark"}
API="https://api.gravixlayer.ai/v1"

curl -sS -X POST "$API/agents/runtime/$RT/display/mouse/click" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"x":640,"y":420,"button":"left"}'

curl -sS -X POST "$API/agents/runtime/$RT/display/mouse/scroll" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dx":0,"dy":-480}'

curl -sS -X POST "$API/agents/runtime/$RT/display/keyboard/type" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"text":"user@example.com"}'

curl -sS -X POST "$API/agents/runtime/$RT/display/keyboard/hotkey" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"keys":["ctrl","l"]}'
```

| Route | Body |
| - | - |
| `mouse/click` | `x`, `y`, `button` (default `left`) |
| `mouse/move` | `x`, `y` |
| `mouse/scroll` | `dx`, `dy` — applied at the current pointer position |
| `keyboard/type` | `text`, optional `delay_ms` between keystrokes |
| `keyboard/press` | `key` — `Enter`, `Tab`, `Escape`, `F1`–`F12`, arrows, or a single character |
| `keyboard/hotkey` | `keys` — e.g. `["ctrl","shift","t"]` |

## Live view

`view` publishes a private watch URL for the screen. Open **`browser_url`** —
it carries a one-time auth token that signs the browser in. The token is only
returned by the first `view` call for the runtime; later calls extend
`expires_at` but return `browser_url` without the token, so keep the first
one. URLs expire after an hour.

```python Python theme={"dark"}
from gravixlayer import GravixLayer

client = GravixLayer()
sandbox = client.runtime.create(template="browser-small")
sandbox.display.enable()

v = sandbox.display.view(fps=10)
print(v["browser_url"])  # open this to watch live
# The URL stops working when the runtime is killed.
```

```typescript TypeScript theme={"dark"}
import { GravixLayer } from 'gravixlayer';

const client = new GravixLayer();
const sandbox = await client.runtime.create({ template: 'browser-small' });
await sandbox.display.enable();

const v = await sandbox.display.view({ fps: 10 });
console.log(v.browserUrl); // open this to watch live
// The URL stops working when the runtime is killed.
```

```bash CLI theme={"dark"}
gravixlayer runtime display view "$RT" --fps 10   # prints browser_url
```

```bash cURL theme={"dark"}
API="https://api.gravixlayer.ai/v1"

curl -sS -X POST "$API/agents/runtime/$RT/display/view" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"fps":10,"quality":80}' | jq -r .browser_url
```

`fps` defaults to 15 and caps at 30; `quality` is JPEG quality 1–100. The call
returns `201` with the web-service record — `browser_url`, `url`, `port`,
`expires_at`, and `token` (first call only). See
[Web Services](/documentation/agentruntime/getting-started/web-services) for how
private URLs authenticate.

## Disable

`disable` stops the display server; headed browser windows go with it.
`enable` starts it again.

```python Python theme={"dark"}
from gravixlayer import GravixLayer

client = GravixLayer()
sandbox = client.runtime.create(template="browser-small")
sandbox.display.enable()

sandbox.display.disable()
print(sandbox.display.status()["enabled"])  # False
sandbox.kill()
```

```typescript TypeScript theme={"dark"}
import { GravixLayer } from 'gravixlayer';

const client = new GravixLayer();
const sandbox = await client.runtime.create({ template: 'browser-small' });
await sandbox.display.enable();

await sandbox.display.disable();
console.log((await sandbox.display.status()).enabled); // false
await sandbox.kill();
```

```bash CLI theme={"dark"}
gravixlayer runtime display disable "$RT"
```

```bash cURL theme={"dark"}
API="https://api.gravixlayer.ai/v1"

curl -sS -X POST "$API/agents/runtime/$RT/display/disable" \
  -H "Authorization: Bearer $GRAVIXLAYER_API_KEY"
```

## Notes

* Display coordinates are absolute screen pixels. `region` uses the same
  space, so a point in a cropped screenshot maps back by adding the region's
  `x` and `y`.
* `wait_stable` beats a fixed sleep: it returns as soon as the screen stops
  moving instead of waiting out the worst case.
* Prefer browser actions over display input for web work — refs survive
  layout shifts where coordinates do not. Reach for the display when the
  target is outside the DOM.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.