JavaScript SDK

โšก Loading a virtual browser

Hyperbeam(container: HTMLDivElement | HTMLIFrameElement, embedURL: string, opts?: HyperbeamOptions): Promise<HyperbeamClient>

Creates a new Hyperbeam client. The container can be either a div or an iframe element, though it is highly advised to use a div. The embedURL is retrieved from the REST API.

// The embedURL is retrieved from the REST API
// Documentation for the REST API can be found here:
const embedURL = ""
const container = document.getElementById("container-id")

const hb = await Hyperbeam(container, embedURL, {
	// Number of milliseconds until the request to the virtual browser times out.
	// If the request times out, the returned promise will be rejected.
	timeout: 5000, // default = 2000

	// An admin token returned from the REST API that will grant this user
	// access to managing user permissions and programmatic navigation.
	adminToken: "admin_token_from_rest_api_response",

	// Starting volume of the virtual browser
	volume: 0.2,         // default = 1.0

	// Starting video pause state of the virtual browser
	videoPaused: false,  // default = false

	// delegate global keyboard events to the embed
	delegateKeyboard: true, // default = true

				// Callback called with the virtual computer's video frame data
	// For Chromium-based browsers, its type is ImageBitmap
	// For other browsers, it's a HTMLVideoElement
	// Most frameworks like Three.js and Babylon.js can handle both types automatically
	frameCb: (frame) => {},

	// Callback called with an MediaStreamTrack of the virtual computer's audio stream
	audioTrackCb: (track) => {},

	// Data to be provided to your webhook endpoint if you're using webhook authentication
	webhookUserdata: {myAppData: {user: "your-app-user-id"}},

	// Callback called when another user moves their mouse cursor on top of the virtual browser
	// Useful for implementing multiplayer cursors
	onCursor({ x, y, userId }) => {},

	// Callback called when the user disconnects from the virtual browser
	// type is an enum with one of the following values:
	//   "request"  -> virtual browser was manually shut down
	//   "inactive" -> inactive timeout was triggered
	//   "absolute" -> absolute timeout was triggered
	//   "kick"     -> user was kicked from the session
	onDisconnect({ type }) => {},

	// Callback called when a timeout either surpasses the warning threshold,
	// or has been reset and is no longer passed the warning threshold.
	// type is an enum that refers to the timeout's type tied to the event
	// possible values are "inactive" and "absolute"
	// deadline = null | { delay: number, closeDate: string }
	// If deadline is null, then the timeout was reset and is no longer
	// passed the warning threshold. If deadline is set, deadline.delay
	// is the number of milliseconds until the timeout is triggered,
	// and closeDate is an RFC3339 formatted string of when the timeout will occur
	onCloseWarning: ({ type, deadline }) => {},

	// Callback called when the connection state of the video stream has changed
	// state = "connecting" | "playing" | "reconnecting"
	// You can use this to show custom reconnecting UI, and detecting if a user
	// has a sub-optimal connection to the virtual browser
	onConnectionStateChange: ({ state }) => {}

The promise returned by the Hyperbeam function might be rejected. See example below:

import Hyperbeam, { TimedOutError, SessionTerminatedError } from "Hyperbeam";

async function main() {
  let hb;
  try {
    hb = await Hyperbeam(container, embedURL, options);
  } catch (e) {
    switch ( {
      case "TimedOutError":
        console.log("Request to load the embed URL timed out", e.message);
      case "TypeError":
          "Invalid options passed into the Hyperbeam constructor",
      case "SessionTerminatedError":
        console.log("Session has already been terminated", e.message);
    // Alternatively, if you can use instanceof
    // e.g. if (e instanceof TimedOutError) { ... }


๐Ÿ’ฃ Destroying the embed


Tears down network connections and browser events. Always call this before removing the container element from the page.

const hb = await Hyperbeam(container, embedURL)

// destroy on clicking button
disconnectBtn.addEventListener("click", () => {

// React
componentWillUnmount() {

// Vue
beforeUnmount() {

๐Ÿ”‰ Setting video volume

hb.volume = 0.5

Sets the volume for the virtual browser locally. Volume changes only apply to the local user. This setting is not persisted on refreshing the page.

const hb = await Hyperbeam(container, embedURL);
// Valid values range from 0.0 to 1.0
hb.volume = 0.5; // default = 1.0
console.log(hb.volume); // Get the local volume value

๐Ÿ†” Getting user ID

hb.userId: string

Gets the clientโ€™s user ID. A โ€œuserโ€ is defined as a single connection to the virtual browser. If a person has multiple tabs connected to the virtual browser, each tab with an active connection will be assigned a different user ID.

const hb = await Hyperbeam(container, embedURL);
console.log(hb.userId); // A unique string identifying the user

โธ๏ธ Pausing video stream

hb.videoPaused = true

Pauses/resumes the video stream for the virtual browser locally. Useful if only the audio component is of interest and you want to minimize CPU usage.

const hb = await Hyperbeam(container, embedURL);
hb.videoPaused = true; // Pause the video stream
hb.videoPaused = false; // Resume the video stream
console.log(hb.videoPaused); // Get the video stream pause state

๐Ÿ‘‘ Setting admin token

hb.adminToken = "adminToken"

Sets the clientโ€™s admin token. The client must have an admin token set to manage user permissions and control the tabs programmatically.

// You can provide the admin token during initialization
const hb = await Hyperbeam(container, embedURL, {
  adminToken: "admin_token_from_rest_api_response",

// In some situations, you may want to promote the user to
// an admin after initialization. For example, promoting a "user" to a "moderator".
const hb = await Hyperbeam(container, embedURL);
hb.adminToken = "admin_token_from_rest_api_response";

๐Ÿ” Setting permissions

hb.setPermission(userId: string, permissionData: PermissionData): Promise<void>

Sets the permission of a user by their ID. The client must have an admin token set to manage user permissions.

// You can provide the admin token during initialization
const hb = await Hyperbeam(container, embedURL, {
  adminToken: "admin_token_from_rest_api_response",

const targetUserId = "a_user_id";

// All keys can be omitted: if the key is omitted, then the existing value will be unchanged
const permissions = {
  // Higher value = higher priority
  // Users with a higher priority will preempt the control of lower priority users.
  priority: 2, // default = 0

  // Number of milliseconds until a user is considered "idle". Once a user is considered
  // idle, they no longer preempt lower priority users until they interact with the
  // virtual browser again.
  idle_timeout: 3000, // default = 0

  // If control_disabled = true, all control input (mouse movements, keyboard presses)
  // will be ignored. Note that disabling control does not restrict access to any
  // APIs that require admin tokens.
  control_disabled: true, // default = control_disable_default (see REST API)

hb.setPermissions(targetUserId, permissions);

๐Ÿ”„ Manual reconnection

hb.reconnect(): void

In situations where you need to troubleshoot the browser disconnecting, adding a manual reconnect button may help for debugging.

manualReconnectBtn.addEventListener("click", () => {

๐Ÿ“ Resizing the browser

hb.resize(width: number, height: number): Promise

Resizes the virtual browser to the specified width and height, in pixels.

The arguments must meet the following conditions, otherwise the function will throw a RangeError: width * height cannot be greater than hb.maxArea.

hb.maxArea is the maximum area that can be allocated in pixels.

if (width * height > hb.maxArea) {
  console.log(`width * height must be less than ${hb.maxArea}`);
} else {
  hb.resize(width, height);

You can retrieve the current width and height by reading hb.width and hb.height.

๐Ÿ“ก Send events programmatically

hb.sendEvent(event: KeyEvent | MouseEvent | WheelEvent): void

Sends a keyboard, mouse, or mouse wheel event to the Hyperbeam browser.

const keyEvent = {
	type: "keydown", // type can be set to "keydown" and "keyup"
	// String denoting which key is pressed.
	// See
	key: "A",
	ctrlKey: true // Boolean indicating if the Ctrl key is pressed. Default is false.
	metaKey: false // Boolean indicating if the Meta key is pressed. Default is false.

const mouseEvent = {
	type: "mousedown" // type can be set to "mousemove", "mousedown", and "mouseup"
	x: 0.5, // The mouse's X position as a ratio, with a range of [0, 1]
	y: 0.5, // The mouse's Y position as a ratio, with a range of [0, 1]
	// Number that indicates which button was pressed on the mouse. Default is 0.
	// See
	button: 2

const wheelEvent = {
	type: "wheel", // Mouse wheel event, used for scrolling up or down
	deltaY: 20 // Positive value signifies scrolling down

hb.sendEvent(keyEvent) // Send CTRL+A keystroke
hb.sendEvent(mouseEvent) // Right-click the center of the screen (0.5, 0.5)
hb.sendEvent(wheelEvent) // Scroll down

โŒจ๏ธ Tighter control over keyboard events

By default, the Hyperbeam client forwards global keydown and keyup events into the embed when an input element is not in focus outside the embed. In some cases, you may want to customize this functionality:

  • Your app has key bindings which conflict with Hyperbeam
  • You have UI flows such as overlays and tabs which hide the Hyperbeam embed from view

You can have the Hyperbeam client disregard these events by setting the delegateKeyboard option to false. Input fields inside the embed (e.g. address bar, form fields) will continue to receive input when they are focused.

const hb = await Hyperbeam(container, embedURL, {
  delegateKeyboard: false,

Moreover, you can combine this with sending keyboard events programmatically to selectively forward keyboard events:

const hb = await Hyperbeam(container, embedURL, {
  delegateKeyboard: false,

function onKeyEvent(e) {
  const { activeElement } = document;
  if (
    // by default, events are ignored if an input element is in focus
    (!activeElement ||
      (activeElement.nodeName !== "INPUT" &&
        activeElement.nodeName !== "TEXTAREA" &&
        !activeElement.isContentEditable)) &&
    // your custom checks go here, for example
    (e.key === " " || e.key === "Enter")
  ) {
      type: e.type,
      key: e.key,
      ctrlKey: e.ctrlKey,
      metaKey: e.metaKey,
window.addEventListener("keydown", onKeyEvent);
window.addEventListener("keyup", onKeyEvent);

๐ŸŽ›๏ธ Control tabs programmatically

Hyperbeamโ€™s tab API mirrors Chrome extension tab API, found here.

Rather than calling chrome.tabs.create({ active: true }), you will would call hb.tabs.create({ active: true }).

We do not support callbacks for accessing return values โ€” tab methods return a Promise

We support the following methods:

<div style="font-family: Arial">
	<button id="reload">Reload</button>
	<button id="back">Go Back</button>
	<button id="forward">Go Forward</button>
	<button id="youtube">Open</button>
	Volume: <input type="range" min="0" max="100" value="100" id="range">
	<p>User ID: <span id="userId"></span></p>
	<p>Current website: <span id="currentSite"></p>
<div id="container" style="height:720px;width:1280px"></div>
<script type="module">
import Hyperbeam from ""
// TODO: Set the embedURL variable
const embedURL = "<your-embed-url>"
const hb = await Hyperbeam(container, embedURL)
userId.innerText = hb.userId
hb.tabs.onUpdated.addListener((tabId, changeInfo) => {
	if (changeInfo.title) {
		currentSite.innerText = changeInfo.title
// Create a tab
const tab = await hb.tabs.create({ active: true })
// Duplicate a tab
const tab2 = await hb.tabs.duplicate(
// Set the URL of the active tab
const updatedTab = await hb.tabs.update({url: ""})

// Button click handlers
reload.addEventListener("click", () => {
back.addEventListener("click", () => {
forward.addEventListener("click", () => {
youtube.addEventListener("click", () => {
	hb.tabs.update({ url: "" })
range.addEventListener("change", (e) => {
	hb.volume = / 100

๐Ÿ‘‚ Listen to tab events

Hyperbeamโ€™s tab event listener API mirrors Chrome extension tab API, found here.

Rather than calling chrome.tabs.onCreated.addListener((tab) => {}), you will would call hb.tabs.onCreated.addListener((tab) => {}).

We support the following events:

const hb = await Hyperbeam(container, embedURL);

function onTabCreated(tab) {

// Listen for when a new tab is created

// Create a new tab
await hb.tabs.create({ active: true });

// Remove the event listener

๐Ÿ—บ๏ธ Optimize server location

To create a session with the optimal server location, you first need to use getRegionInfo() on the client to retrieve the optimal region information.

From there, you need to send the HyperbeamRegionInfo objectโ€™s region value to your backend, and use it as the argument for the region parameter when creating a session and generating an embed_url.

You may also want to cache the region preference in localStorage, since getRegionInfo() fires off a web request.


import Hyperbeam, { getRegionInfo } from "@hyperbeam/web";

async function createHyperbeam(container) {
  const regionInfo = await getRegionInfo();
  // Pass region code to backend to be used during session creation
  const embedURL = await fetch(`/myapi?region=${regionInfo.region}`, {
    method: "POST",
  const hb = Hyperbeam(container, embedURL);
  return hb;