Skip to main content
Execute arbitrary Playwright/TypeScript code in a fresh execution context against your browser. The code runs in the same VM as the browser, minimizing latency and maximizing throughput. For complex workloads, Kernel has a full code execution platform.

How it works

When you execute Playwright code through this API:
  • Your code runs directly in the browser’s VM (no CDP overhead)
  • You have access to page, context, and browser variables
  • You can return a value, which is returned in the response
  • Execution is isolated in a fresh context each time

Quick example

import Kernel from '@onkernel/sdk';

const kernel = new Kernel();

// Create a browser
const kernelBrowser = await kernel.browsers.create();

// Execute Playwright code
const response = await kernel.browsers.playwright.execute(
  kernelBrowser.session_id,
  {
    code: `
      await page.goto('https://example.com');
      return await page.title();
    `
  }
);

console.log(response.result); // "Example Domain"
from kernel import Kernel

kernel = Kernel()

# Create a browser
kernel_browser = kernel.browsers.create()

# Execute Playwright code
response = kernel.browsers.playwright.execute(
    id=kernel_browser.session_id,
    code="""
        await page.goto('https://example.com')
        return await page.title()
    """
)

print(response.result)  # "Example Domain"
package main

import (
	"context"
	"fmt"

	"github.com/kernel/kernel-go-sdk"
)

func main() {
	ctx := context.Background()
	client := kernel.NewClient()

	// Create a browser
	kernelBrowser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{})
	if err != nil {
		panic(err)
	}

	// Execute Playwright code
	response, err := client.Browsers.Playwright.Execute(ctx, kernelBrowser.SessionID, kernel.BrowserPlaywrightExecuteParams{
		Code: `
			await page.goto('https://example.com');
			return await page.title();
		`,
	})
	if err != nil {
		panic(err)
	}

	fmt.Println(response.Result) // "Example Domain"
}
kernel browsers playwright execute <session_id> 'await page.goto("https://www.onkernel.com"); return page.title();'

Available variables

Your code has access to these Playwright objects:
  • page - The current page instance
  • context - The browser context
  • browser - The browser instance

Returning values

Use a return statement to send data back from your code:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://example.com');
      const title = await page.title();
      const url = page.url();
      return { title, url };
    `
  }
);

console.log(response.result); // { title: "Example Domain", url: "https://example.com" }
response = kernel.browsers.playwright.execute(
    id=session_id,
    code="""
        await page.goto('https://example.com')
        title = await page.title()
        url = page.url()
        return {'title': title, 'url': url}
    """
)

print(response.result)  # {'title': 'Example Domain', 'url': 'https://example.com'}
response, err := client.Browsers.Playwright.Execute(ctx, sessionID, kernel.BrowserPlaywrightExecuteParams{
	Code: `
		await page.goto('https://example.com');
		const title = await page.title();
		const url = page.url();
		return { title, url };
	`,
})
if err != nil {
	panic(err)
}

fmt.Println(response.Result) // map[title:Example Domain url:https://example.com]

Timeout configuration

Set a custom timeout (default is 60 seconds, max is 300 seconds):
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://example.com');
      return await page.title();
    `,
    timeout_sec: 120
  }
);
response = kernel.browsers.playwright.execute(
    id=session_id,
    code="""
        await page.goto('https://example.com')
        return await page.title()
    """,
    timeout_sec=120
)
response, err := client.Browsers.Playwright.Execute(ctx, sessionID, kernel.BrowserPlaywrightExecuteParams{
	Code: `
		await page.goto('https://example.com');
		return await page.title();
	`,
	TimeoutSec: kernel.Int(120),
})
if err != nil {
	panic(err)
}
_ = response

Error handling

The response includes error information if execution fails:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://invalid-url');
      return await page.title();
    `
  }
);

if (!response.success) {
  console.error('Error:', response.error);
  console.error('Stderr:', response.stderr);
}
response = kernel.browsers.playwright.execute(
    id=session_id,
    code="""
        await page.goto('https://invalid-url')
        return await page.title()
    """
)

if not response.success:
    print('Error:', response.error)
    print('Stderr:', response.stderr)
response, err := client.Browsers.Playwright.Execute(ctx, sessionID, kernel.BrowserPlaywrightExecuteParams{
	Code: `
		await page.goto('https://invalid-url');
		return await page.title();
	`,
})
if err != nil {
	panic(err)
}

if !response.Success {
	fmt.Println("Error:", response.Error)
	fmt.Println("Stderr:", response.Stderr)
}

Use cases

Web scraping

Extract data from multiple pages without CDP overhead:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://news.ycombinator.com');
      const titles = await page.$$eval('.titleline > a', 
        links => links.map(link => link.textContent)
      );
      return titles.slice(0, 10);
    `
  }
);

Form automation

Fill and submit forms quickly:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://example.com/form');
      await page.fill('#email', 'user@example.com');
      await page.fill('#password', 'password123');
      await page.click('button[type="submit"]');
      await page.waitForNavigation();
      return page.url();
    `
  }
);

Testing and validation

Run quick checks against your browser state:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      const cookies = await context.cookies();
      const localStorage = await page.evaluate(() => 
        JSON.stringify(window.localStorage)
      );
      return { cookies, localStorage };
    `
  }
);

Screenshots

Capture screenshots using Playwright’s native screenshot API:
const response = await kernel.browsers.playwright.execute(
  sessionId,
  {
    code: `
      await page.goto('https://example.com');
      const screenshot = await page.screenshot({ 
        type: 'png',
        fullPage: true 
      });
      return screenshot.toString('base64');
    `
  }
);

// Decode and save the screenshot
const buffer = Buffer.from(response.result, 'base64');
fs.writeFileSync('screenshot.png', buffer);
For OS-level screenshots using coordinates and regions, see Computer Controls.

Performance benefits

Compared to connecting over CDP:
  • Lower latency - Code runs in the same VM as the browser
  • Higher throughput - No websocket overhead for commands
  • Simpler code - No need to manage CDP connections
This makes it ideal for one-off operations where you need maximum speed.

MCP server integration

This feature is available as a tool in our MCP server. AI agents can use the execute_playwright_code tool to run Playwright code against browsers with automatic video replay and cleanup.