What Is @escrcpy/wscrcpy? Architecture and Purpose in Escrcpy

@escrcpy/wscrcpy is the core WebSocket Scrcpy package that powers the Escrcpy Electron application, providing a self-contained Vue and TypeScript module implementing the full Scrcpy protocol over WebSockets with strict separation between renderer and main-process APIs.

The viarotel-org/escrcpy repository uses @escrcpy/wscrcpy to encapsulate all Android screen mirroring and device control logic. This package exposes a clean boundary between the Electron main process (handling TCP streams via ADB) and the Vue-based renderer (displaying video and capturing input), ensuring the protocol stack remains isolated from UI concerns.

Dual-Entry Architecture

The package enforces a strict process-separation model through two distinct entry points. According to AGENTS.md, the package root exports the renderer-facing API (Vue components, composables, and utilities), while @escrcpy/wscrcpy/main exports the ByteBridge factory for the main process.

This design prevents accidental cross-process imports: the renderer must never import the main entry, and the main process must never import the renderer root. The boundary ensures that low-level TCP socket handling stays in the main process while UI interactions remain in the Chromium sandbox.

Core Components and File Structure

The ByteBridge Service

In packages/wscrcpy/service/bridge/index.ts, the ByteBridge implements a thin TCP ⇄ MessagePort pump that transports raw Scrcpy streams between the Android device (accessed via ADB) and the renderer process. As noted in AGENTS.md, this service layer runs entirely in the main process and manages back-pressure handling for video and audio streams.

Protocol Stack Implementation

The Scrcpy protocol logic—including packet parsing, channel multiplexing, and sequence handling—resides in packages/wscrcpy/src/stack/. This directory contains the full state machine required to decode H.264 video frames and inject input events, operating independently of Electron APIs.

Runtime Channel Routing

packages/wscrcpy/src/core/runtime.ts routes every incoming WebSocket channel to the correct local stack instance based on the active window. As documented in AGENTS.md, this runtime acts as a demultiplexer, ensuring that multiple concurrent device sessions do not interfere with each other.

Practical Usage Examples

Renderer-Side Component Integration

Import the Wscrcpy Vue component from the package root to render the device screen and handle user input:

import { defineComponent } from 'vue'
import Wscrcpy from '@escrcpy/wscrcpy'

export default defineComponent({
  components: { Wscrcpy },
  template: `
    <div class="device-container">
      <Wscrcpy :device-id="deviceId" :options="scrcpyOptions" />
    </div>
  `,
  data() {
    return {
      deviceId: 'emulator-5554',
      scrcpyOptions: {
        videoCodec: 'h264',
        maxFps: 60,
        turnScreenOff: true
      }
    }
  }
})

Connection Management Composable

For imperative control over sessions, use the useWscrcpyConnection composable exported from packages/wscrcpy/src/composables/useWscrcpyConnection.ts:

import { useWscrcpyConnection } from '@escrcpy/wscrcpy'

const { start, stop, status, error } = useWscrcpyConnection({
  deviceId: '192.168.1.5:5555',
  audio: true,
  clipboardAutosync: false,
  displayId: 0
})

// Start the mirroring session
await start()

// Check connection state
console.log('Connection status:', status.value)

// Gracefully terminate
await stop()

Main-Process Bridge Initialization

In the Electron main process, initialize the TCP bridge using the dedicated /main entry point:

import { createWscrcpyByteBridge } from '@escrcpy/wscrcpy/main'
import path from 'path'

const bridge = createWscrcpyByteBridge({
  adbPath: path.join(process.resourcesPath, 'platform-tools', 'adb'),
  scrcpyPath: path.join(process.resourcesPath, 'scrcpy', 'scrcpy'),
  maxBuffer: 1024 * 1024 // 1MB buffer for video frames
})

// Register IPC handlers to communicate with renderer
bridge.registerIpcHandlers()

Configuration Types and Constants

DeviceTarget and Channel Contracts

The package defines strict TypeScript contracts for device targeting. According to SKILL.md, the DeviceTarget type accepts 'all', 'primary', a specific serial string, or an array of serials. Additionally, the SCRCPY_CHANNELS constants define the official IPC channel names that must be used across the application boundary.

Audio and Clipboard Defaults

Audio forwarding is opt-in by default on macOS systems due to platform limitations, as specified in AGENTS.md. The createScrcpyOptions helper in packages/wscrcpy/src/options.ts builds command-line arguments respecting these defaults, allowing developers to explicitly enable audio or disable clipboard autosync through the options interface.

Summary

  • @escrcpy/wscrcpy encapsulates the entire Scrcpy protocol implementation as a reusable Vue/TypeScript module within the Escrcpy ecosystem.
  • The package enforces strict process separation via two entry points: the renderer API (package root) and the main-process ByteBridge (@escrcpy/wscrcpy/main).
  • Service/bridge/ handles the TCP ⇄ MessagePort pump in the main process, while src/stack/ contains the protocol decoding logic.
  • Runtime routing in src/core/runtime.ts manages multiple concurrent device sessions without cross-contamination.
  • Configuration follows strict types including DeviceTarget and respects platform-specific defaults like macOS audio opt-in.

Frequently Asked Questions

What is the difference between importing from @escrcpy/wscrcpy versus @escrcpy/wscrcpy/main?

The package root (@escrcpy/wscrcpy) exports Vue components, composables, and renderer utilities that run inside the Chromium sandbox. The @escrcpy/wscrcpy/main entry exports the ByteBridge factory and low-level TCP handlers that must only execute in the Node.js-backed Electron main process. Importing the wrong entry into the wrong process will break IPC communication and likely crash the application due to missing Node APIs in the renderer or security restrictions in the main process.

How does @escrcpy/wscrcpy handle audio forwarding from Android devices?

Audio support is implemented in the protocol stack (src/stack/) but is disabled by default on macOS as documented in AGENTS.md. Developers must explicitly set audio: true in the connection options. The audio stream travels through the same ByteBridge service as video, but uses separate channel multiplexing to maintain synchronization. On Linux and Windows, audio may be enabled by default depending on the Scrcpy binary version bundled with the application.

Where is the Scrcpy protocol logic actually implemented?

The complete protocol implementation—including H.264 packet parsing, input event injection, and clipboard synchronization—lives in packages/wscrcpy/src/stack/. This directory operates as a standalone state machine that processes raw TCP streams from the ByteBridge service. The runtime.ts file in src/core/ then routes these decoded frames and events to the appropriate Vue component instance based on the active window ID.

Can I use @escrcpy/wscrcpy outside of the Escrcpy Electron application?

While the package is architected for Electron’s main/renderer split, the protocol stack (src/stack/) and option builders (src/options.ts) are framework-agnostic TypeScript modules. However, the ByteBridge service specifically relies on Electron’s IPC (MessagePort) and Node.js socket APIs. To use the package in a different environment (such as a plain Node.js CLI or a different UI framework), you would need to reimplement the bridge service while retaining the protocol stack and type definitions.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →