VibeView SDK
The VibeView SDK lets you embed live iOS and Android simulators directly into your website, documentation, or web application. Users can interact with a real device stream right in the browser.
Overview
The SDK handles the full lifecycle of a simulator session: creating it, connecting to the video stream, capturing user interactions, and cleaning up resources when done. It supports two usage patterns:
- React component — A
<VibeView />wrapper for React applications (available via the@vibeview/sdk/reactsubpath). - Vanilla JS client — The
VibeViewClientclass for any JavaScript environment.
Both approaches use the same underlying client and support the same configuration options.
Installation
Install the SDK from npm:
npm install @vibeview/sdk
Or with Yarn:
yarn add @vibeview/sdk
The SDK requires Node.js 14+ and works in modern browsers with WebCodecs or Media Source Extensions support (Chrome 94+, Edge 94+, Safari 16.4+).
React Usage
Import the component from the @vibeview/sdk/react subpath:
import { VibeView } from '@vibeview/sdk/react';
function SimulatorDemo() {
return (
<VibeView
deviceType="ios"
token="your-auth-token"
autoStart={true}
style={{ width: 375, height: 812 }}
/>
);
}
React is an optional peer dependency. The SDK works without React installed — the core @vibeview/sdk import has no React dependency.
Vanilla JS Usage
Use the VibeViewClient class directly when working outside React:
import { VibeViewClient } from '@vibeview/sdk';
const client = new VibeViewClient('#simulator-container', {
apiUrl: 'https://vibeview.io',
deviceType: 'ios',
token: 'your-auth-token',
autoStart: true,
});
await client.ready();
client.on('session:started', (session) => {
console.log('Session started:', session.sessionId);
});
The first argument is a CSS selector string or an HTMLElement reference where the simulator will be rendered.
There is also a convenience helper that creates the client and waits for it to be ready in one call:
import { vibeview } from '@vibeview/sdk';
const client = await vibeview('#container', {
deviceType: 'android',
autoStart: true,
});
Configuration
Pass a configuration object when creating a client. All properties are optional.
| Property | Type | Default | Description |
|---|---|---|---|
apiUrl | string | https://vibeview.io | Backend API URL. |
wsUrl | string | wss://vibeview.io | WebSocket URL for streaming. |
token | string | — | Bearer token for authentication. |
sessionId | string | — | Connect to an existing session instead of creating a new one. |
deviceType | 'ios' | 'android' | 'roku' | — | Device platform to launch. For TV, combine with deviceCategory: 'tv' (Apple TV = 'ios' + 'tv'; Android TV = 'android' + 'tv'). 'roku' streams a Roku device (beta). |
deviceCategory | 'phone' | 'tablet' | 'tv' | omitted = any handheld | Device form factor. Omit to allocate any handheld (any phone or tablet); pass 'tv' for Apple TV / Android TV sessions, or 'phone'/'tablet' to narrow. |
deviceId | string | — | Request a specific device by ID. |
autoStart | boolean | false | Start the session immediately on initialization. |
scale | number | 'auto' | 'auto' | Device scale factor (10—100), or 'auto' to fit the container. |
screenOnly | boolean | false | Show only the screen without device chrome. |
deviceColor | 'black' | 'white' | 'black' | Device chrome color. |
orientation | 'portrait' | 'landscape' | 'portrait' | Initial device orientation. |
displayRotation | 0 | 90 | 180 | 270 | 0 | Visual rotation (degrees clockwise) the host applies to the canvas. The SDK inverse-transforms taps so they still land on the correct device pixels. Useful when displaying a landscape-locked app upright inside a portrait canvas. |
readOnly | boolean | false | Disable user interactions (view-only mode). |
decoder | 'webcodecs' | 'mse' | 'webcodecs' | Video decoder. WebCodecs has lower latency; MSE is more broadly compatible. |
enableAdaptiveQuality | boolean | true | Automatically adjust bitrate/FPS quality tiers to match network conditions. |
enableHEVC | boolean | true | Negotiate HEVC (H.265) when the browser supports it; falls back to H.264 otherwise. |
debug | boolean | false | Enable debug logging. |
className | string | — | Custom CSS class for the container element. |
style | Partial<CSSStyleDeclaration> | — | Inline styles for the container element. |
Canvas Options
The canvas property accepts an object for fine-tuning rendering:
| Property | Type | Default | Description |
|---|---|---|---|
accelerated | boolean | true | Use hardware acceleration. |
imageSmoothingEnabled | boolean | true | Enable image smoothing when scaling. |
Events
The client emits events you can listen to with client.on():
client.on('session:status', (status) => { /* pending, starting, active, stopped, failed */ });
client.on('session:started', (session) => { /* session info with sessionId, deviceType, etc. */ });
client.on('session:failed', (error) => { /* Error object */ });
client.on('session:ended', () => { /* session stopped */ });
client.on('stream:started', () => { /* video frames arriving */ });
client.on('stream:stopped', () => { /* video stream ended */ });
client.on('stream:frame', (frameCount) => { /* a video frame was received (for custom rendering) */ });
client.on('stream:dimensions', ({ width, height }) => { /* video dimensions changed, e.g. after rotation */ });
client.on('connection:state', (state) => { /* connecting, connected, disconnected, reconnecting, error */ });
client.on('connection:failed', () => { /* reconnection gave up after all retry attempts */ });
client.on('error', (error) => { /* general error */ });
client.on('interaction', (action) => { /* user tap, swipe, etc. */ });
client.on('interaction:enhanced', (action) => { /* recorded action enriched with element context */ });
client.on('device:orientation', (orientation) => {
/* 'portrait' | 'portrait_upside_down' | 'landscape_left' | 'landscape_right' — iOS only */
});
Device Control
The client exposes programmatic device control alongside direct user interaction. All methods require an active session.
await client.tap(160, 320);
await client.swipe(160, 600, 160, 200, 300);
await client.typeText('hello@example.com');
| Method | Description |
|---|---|
tap(x, y) | Tap at coordinates (pixels in the streamed video). |
swipe(x1, y1, x2, y2, duration?) | Swipe from one point to another; duration in milliseconds (default 500). |
typeText(text) | Type a full string as a single command. |
type(text) | Legacy variant: sends the string character by character. |
pressKey(keycode) | Press a hardware key (Android/TV), e.g. 'BACK', 'HOME', 'DPAD_UP', 'DPAD_CENTER'. |
pressButton(button) | Press an iOS hardware button, e.g. 'HOME', 'LOCK'. |
rotate(degrees?) | Rotate the device; 90, 180, or 270 (default 90). |
setLocation(lat, lon) | Set the simulated GPS location. |
setAppearance(mode) | Switch the device between 'light' and 'dark' appearance. |
screenshot(options?) | Capture the current frame; returns a data-URL string. Options: format ('png' default, 'jpeg', 'webp') and quality (0–1, default 0.92). |
installApp(options) | Install an app into the session. Pass file (a File/Blob) or url (downloaded first), plus optional launchAfterInstall (default false). |
TV devices
On TV sessions (Apple TV / Android TV), interaction is focus-based — the platform walks the d-pad focus to the target element instead of tapping coordinates:
| Method | Description |
|---|---|
tapFocusedTv(locator) | Move d-pad focus to the element matching the locator and press SELECT to activate it. |
focusElementTv(locator) | Move d-pad focus to the element without activating it. |
Both take a locator object with any of accessibilityId, resourceId, text, and rect ({ x, y, width, height }). Both are fire-and-forget: they resolve when the command is sent, not when the walk completes.
Live keyboard
For live typing from a physical keyboard (as opposed to programmatic typeText), the client provides buffer-aware keystroke methods. Keystrokes are injected to the device immediately; while recording, consecutive characters are aggregated into a single type_text action instead of one action per keypress:
| Method | Description |
|---|---|
liveType(char) | Inject a single printable character. While recording, it accumulates in a pending buffer and fires a recording:buffer event so UIs can show in-flight text. |
liveKey(keycode) | Inject 'RETURN' or 'BACKSPACE'. RETURN flushes the pending buffer as one type_text action, then records a discrete key press; BACKSPACE pops the last pending character (or records a discrete key press if the buffer is empty). |
flushKeyboardBuffer() | Commit any pending buffered text as a single aggregated type_text action. Idempotent — an empty buffer is a no-op. Called automatically when another action interrupts typing and when recording stops. |
Enhanced Recording
Enhanced recording captures user interactions together with the UI element under each interaction (accessibility ID, text, bounds). The resulting action list is what powers Generate AI Test — upload it to have the AI turn a manual session into a repeatable test — and hybrid replay.
await client.startEnhancedRecording();
// ... user interacts with the device ...
const actions = await client.finalizeEnhancedRecording();
console.log(client.exportEnhancedRecording()); // formatted JSON for debugging
| Method | Description |
|---|---|
startEnhancedRecording() | Begin recording with element context. Async — it pre-warms the element lookup so the first tap is captured accurately; disable your record button until it resolves. |
stopEnhancedRecording() | Stop recording and return the captured EnhancedRecordedAction[]. |
finalizeEnhancedRecording(settleMs?) | Stop recording, wait for the screen to settle (default 300 ms), capture a final end-state screenshot, and return the actions. Prefer this over stopEnhancedRecording() when the recording will be used for AI test generation or visual comparison. |
exportEnhancedRecording() | Return the current recording as a formatted JSON string — handy for debugging or sharing what was recorded. |
To let users delete a step from a recording (e.g. in a review UI), use the standalone removeActionAt(actions, index) export — it removes the action at index (including the whole gesture, when the action is part of one) and returns a new array, so it works with React state or imported recordings.
Stream Stats
Inspect stream health and negotiated quality:
| Method | Description |
|---|---|
getStreamStats() | Current decoder statistics: { fps, framesDecoded, framesDropped, decodeErrors, decoderState, decoderType }, or null before the stream starts. Works with both WebCodecs and MSE decoders. |
getPreferredCodec() | The negotiated video codec: 'h264' or 'h265'. |
getQualityTier() | The name of the current adaptive-quality tier. |
Cleanup
Call destroy() when you are done with a client instance:
client.destroy();
The destroy() method is idempotent — it checks an internal this.destroyed flag and returns immediately if the client has already been destroyed. This means it is safe to call destroy() multiple times, for example in both a cleanup handler and a component unmount.
Internally, destroy() closes the WebSocket connection, stops video decoding, removes all event listeners, and clears the container element.
Browser Compatibility
Check whether the current browser supports the SDK before rendering:
import { isSupported, getCompatibilityInfo } from '@vibeview/sdk';
if (!isSupported()) {
const info = getCompatibilityInfo();
console.warn(info.recommendation);
}
The SDK requires WebCodecs (or MSE as a fallback), WebSocket, and Canvas support.
Backward Compatibility
The SDK maintains strict backward compatibility. Published exports are never removed or renamed. If a type or function needs to change, a new export is added alongside the existing one. This ensures that applications built against earlier versions of the SDK continue to work after upgrades.
Tips
- Use
autoStart: truefor demos and embedded docs where you want the simulator to appear immediately. - Use
readOnly: truefor presentation or documentation contexts where viewers should not interact with the device. - Use
screenOnly: trueto remove device chrome and show just the simulator screen, which is useful for tight layouts. - Set
decoder: 'mse'if you need to support older browsers that lack WebCodecs. - The
sessionIdoption lets you connect multiple clients to the same running session, which is useful for shared viewing scenarios. - After changing SDK source types locally, rebuild with
cd sdk && yarn buildbefore consuming the updated package.