BrowserLab is a full-featured, browser-based IDE that executes JavaScript, Python, and WebAssembly entirely in the browser using Web Workers, Pyodide, and native WebAssembly APIs — no server required. It features a virtual file system, package management, performance monitoring, and drag-and-drop file uploads.
Developed by Kautilya Reddy Racha
- Architecture Overview
- Getting Started
- Project Structure
- Phase 1 — Monaco Editor & JavaScript Sandbox
- Phase 2 — Virtual File System (VFS)
- Phase 3 — WebAssembly Runtime
- Phase 4 — Python via Pyodide
- Phase 5 — Unified Multi-Language Runtime Abstraction
- Phase 6 — Execution Sandboxing & Resource Management
- Phase 7 — Package & Dependency Management
- Phase 8 — Performance Optimization & Monitoring
- React UI Layer
- Configuration Reference
- API Reference
- Data Flow Diagrams
- IndexedDB Schema Reference
- Security Model
- Build & Deployment
BrowserLab runs 100% client-side. All code execution happens in the browser via Web Workers and WebAssembly — zero backend required.
┌─────────────────────────────────────────────────────────┐
│ React UI Layer │
│ Root.tsx → HomePage / App.tsx, Components, Hooks │
├──────────┬──────────┬───────────┬────────────┬──────────┤
│ VFS │ Runtime │ Deps │ Perf │ Home │
│ Module │ Manager │ Manager │ Manager │ Page │
├──────────┴──────────┴───────────┴────────────┴──────────┤
│ Web Workers / Service Worker │
│ sandbox.worker.ts │ python.worker.ts │ cache-sw │
├─────────────────────────────────────────────────────────┤
│ Browser APIs │
│ IndexedDB │ WebAssembly │ Cache API │ Drag & Drop │
└─────────────────────────────────────────────────────────┘
| Layer | Technology |
|---|---|
| Build tool | Vite 7.3.1 |
| UI Framework | React 19.2.0 |
| Language | TypeScript ~5.9.3 (strict mode) |
| Code Editor | Monaco Editor 0.55.1 (@monaco-editor/react 4.7.0) |
| Python Runtime | Pyodide 0.26.4 (CDN) |
| Persistence | IndexedDB (3 databases) |
| Caching | Service Worker (Cache API) + in-memory LRU |
| Deployment | Vercel (static) |
- Node.js 20+ and npm
git clone <repo-url>
cd BrowserLab
npm installnpm run dev # Vite dev server with HMRnpm run build # TypeScript check + Vite build
npm run preview # Preview production buildThe project includes a vercel.json with proper SPA routing and COOP/COEP headers for Pyodide/WASM:
# Push to GitHub, then import in Vercel — auto-detects Vite
# Or use the Vercel CLI:
npx vercel| Script | Description |
|---|---|
dev |
Vite dev server with HMR |
build |
tsc -b && vite build |
preview |
Preview production build |
lint |
ESLint check |
BrowserLab/
├── index.html # Entry HTML
├── package.json # Dependencies & scripts
├── vercel.json # Vercel deployment config (SPA + COOP/COEP headers)
├── vite.config.ts # Vite configuration
├── tsconfig.json # Root TypeScript config
├── tsconfig.app.json # App TypeScript config (src/)
├── tsconfig.node.json # Node TypeScript config
├── eslint.config.js # ESLint configuration
├── public/
│ ├── cache-sw.js # Service worker (3 caching strategies)
│ └── vite.svg # Favicon
├── src/
│ ├── main.tsx # React entry point → Root
│ ├── Root.tsx # Page router (HomePage ↔ Editor)
│ ├── App.tsx # Editor layout (Toolbar + Sidebar + Editor + Console)
│ ├── App.css # Global styles
│ ├── index.css # Base styles / CSS variables
│ ├── types.ts # Shared UI types (ConsoleEntry, WorkerResponse)
│ ├── vfs/ # Virtual File System
│ │ ├── types.ts # VFSNode, VFSProject, IFileSystem
│ │ ├── VirtualFileSystem.ts # In-memory IFileSystem implementation
│ │ ├── persistence.ts # IndexedDB CRUD operations
│ │ └── index.ts # Barrel exports
│ ├── runtime/ # Multi-language runtime engine
│ │ ├── types.ts # RuntimeType, IRuntime, ExecutionRequest
│ │ ├── BaseRuntime.ts # Abstract base with state machine
│ │ ├── JavaScriptRuntime.ts # Web Worker sandbox execution
│ │ ├── WasmRuntime.ts # WAT compiler + WASM execution
│ │ ├── PythonRuntime.ts # Pyodide worker execution
│ │ ├── RuntimeManager.ts # Central orchestrator & dispatcher
│ │ ├── ResourceMonitor.ts # Real-time resource tracking
│ │ ├── ExecutionGuard.ts # Auto-terminate on resource breach
│ │ ├── errors.ts # Error normalization per language
│ │ ├── wat-compiler.ts # WAT → WASM binary compiler
│ │ ├── index.ts # Barrel exports
│ │ └── hybrid/ # Execution routing
│ │ ├── types.ts # DispatchPolicy, protocol types
│ │ ├── CodeAnalyzer.ts # Pattern-based code analysis
│ │ ├── FallbackDispatcher.ts # Local routing decision engine
│ │ └── index.ts
│ ├── deps/ # Package & dependency management
│ │ ├── types.ts # PackageSpec, PackageRecord, IPackageResolver
│ │ ├── DependencyManager.ts # Central orchestrator
│ │ ├── DependencyValidator.ts # Code analysis + manifest bridging
│ │ ├── PackageCache.ts # IndexedDB package persistence
│ │ ├── index.ts
│ │ └── resolvers/
│ │ ├── JavaScriptResolver.ts # esm.sh CDN resolver
│ │ ├── PythonResolver.ts # Pyodide/micropip resolver
│ │ ├── WasmResolver.ts # WASM package resolver
│ │ └── index.ts
│ ├── perf/ # Performance monitoring
│ │ ├── types.ts # ProfileSpan, MetricPoint, CacheEntry, etc.
│ │ ├── PerformanceProfiler.ts # Span-based profiling engine
│ │ ├── IntelligentCache.ts # 2-tier LRU/LFU/TTL cache
│ │ ├── MetricsCollector.ts # Time-series metrics aggregation
│ │ ├── PerformanceManager.ts # Central facade
│ │ ├── serviceWorkerRegistration.ts # SW registration utilities
│ │ └── index.ts
│ ├── components/ # React UI components
│ │ ├── HomePage.tsx # Landing page with feature cards
│ │ ├── HomePage.css # Landing page styles
│ │ ├── CodeEditor.tsx # Monaco editor wrapper
│ │ ├── ConsoleOutput.tsx # Output display panel
│ │ ├── Toolbar.tsx # Top bar with controls
│ │ ├── FileExplorer.tsx # VFS file tree + drag-and-drop upload
│ │ ├── DependencyPanel.tsx # Package management UI
│ │ └── PerformancePanel.tsx # 4-tab monitoring dashboard
│ ├── hooks/ # React hooks
│ │ ├── useCodeExecution.ts # Runtime manager bridge
│ │ └── usePerformance.ts # Performance manager bridge
│ ├── workers/ # Web Workers
│ │ ├── sandbox.worker.ts # JavaScript sandbox
│ │ └── python.worker.ts # Pyodide Python runtime + micropip
│ └── context/
│ └── VFSContext.tsx # React Context for VFS + IndexedDB auto-save
└── server/ # (Legacy — not used in serverless mode)
└── src/
├── index.ts # WebSocket execution server
├── container.ts # Docker/subprocess execution
├── session.ts # Session tracking
├── collab.ts # Collaboration server
└── warmPool.ts # Container pre-warming pool
The foundation of BrowserLab: a Monaco-powered code editor with JavaScript execution in an isolated Web Worker sandbox.
Wraps @monaco-editor/react with:
- Dynamic language detection from file extension
- Keyboard shortcut
Ctrl+Enterto trigger execution - Theme:
vs-dark - Automatic layout on resize
A dedicated Web Worker that:
- Blocks dangerous globals —
fetch,XMLHttpRequest,WebSocket,navigator,indexedDB, etc. are overridden withObject.definePropertyto throw on access - Captures console output —
console.log/warn/error/infoare replaced with functions that post messages back to the main thread - Executes code via
new Function('console', code)— runs user code in the worker's isolated scope - Timeout protection — a configurable timeout (default: 5000ms) kills execution of infinite loops
- Memory heartbeat — periodic
performance.memorysampling (Chromium) posted to main thread
Shared types for the worker protocol:
interface ConsoleEntry {
id: string;
type: 'log' | 'warn' | 'error' | 'info' | 'result' | 'system';
content: string;
timestamp: number;
}
interface WorkerResponse {
type: 'console' | 'result' | 'error' | 'timeout';
method?: string;
content: string;
}An in-memory file system with IndexedDB persistence, supporting multi-file projects with tree structure.
type VFSNodeId = string;
interface VFSNode {
id: VFSNodeId;
name: string;
type: 'file' | 'directory';
parentId: VFSNodeId | null; // null = root-level
content: string; // file content (empty for directories)
language: string; // inferred from extension
createdAt: number;
updatedAt: number;
}
interface VFSProject {
id: string;
name: string;
nodes: Record<VFSNodeId, VFSNode>;
activeFileId: VFSNodeId | null;
createdAt: number;
updatedAt: number;
}Implements IFileSystem as a pure in-memory store backed by a VFSProject object. Key features:
- Recursive path building for
getProjectFiles() inferLanguage()maps file extensions to Monaco language IDs- Node ID generation via timestamp + random suffix
IndexedDB database: browserlab_vfs (version 1)
| Operation | Function |
|---|---|
| Save project | saveProject(project: VFSProject) |
| Load project | loadProject(id: string): VFSProject | null |
| List projects | listProjects(): { id, name }[] |
| Delete project | deleteProject(id: string) |
| Save default ID | saveDefaultProjectId(id: string) |
| Get default ID | getDefaultProjectId(): string | null |
React Context provider that:
- Loads the default project from IndexedDB on mount (or creates a starter project)
- Auto-saves to IndexedDB on every content change (debounced)
- Exposes
useVFS()hook with:activeFile,activeFileId,updateFileContent,getActiveFileContent,getProjectFiles,selectFile,createFile,createDirectory,deleteNode,renameNode
On first launch, creates a default project with:
main.js— Hello World with example codeutils.js— Utility functionsmath.wat— WebAssembly text format exampleexample.py— Python exampleREADME.md— Project readme
Executes WebAssembly Text Format (WAT) code by compiling it to WASM binary and running it through the browser's native WebAssembly APIs.
A hand-written WAT text → WASM binary compiler that:
- Tokenizes WAT source into a token stream
- Parses tokens into an AST (module → functions, exports, imports, memory, globals, data segments)
- Emits valid WASM binary format with proper section encoding
- Supports:
i32,i64,f32,f64types - Supports instructions:
local.get/set/tee,global.get/set, arithmetic, comparisons, control flow (if/else/block/loop/br/br_if), memory operations,call,return,drop,select, etc.
Execution flow:
- Detect WAT format (must start with
(module) - Compile WAT → WASM binary via
compileWat() - Set up import object with console-like output functions
- Instantiate module via
WebAssembly.compile()+WebAssembly.instantiate() - Auto-execute: search for
main,_start,run,_mainentry points - Timeout protection: 10 second limit
Python execution powered by Pyodide — a CPython interpreter compiled to WebAssembly, running in a dedicated Web Worker.
- Sandbox hardening — blocks dangerous globals (
XMLHttpRequest,EventSource,indexedDB,caches,BroadcastChannel,SharedWorker). AllowsimportScripts,navigator,location(required by Pyodide internals). - Lazy initialization — Pyodide loaded from
https://cdn.jsdelivr.net/pyodide/v0.26.4/full/on first execution - stdout/stderr capture —
py.setStdout()andpy.setStderr()pipe Python output to the main thread - VFS sync — project files written to Pyodide's emscripten filesystem at
/project/and added tosys.path - Auto-import —
py.loadPackagesFromImports(code)pre-loads required Pyodide-compatible packages - micropip integration — pre-loaded; packages specified in
ExecutionRequest.packagesare installed from PyPI before execution - Package installation — tries
loadPackage()first (Pyodide builtins), then falls back tomicropip.install()(pure-Python wheels from PyPI)
The following popular packages are available as Pyodide builtins (no micropip needed):
numpy, scipy, pandas, matplotlib, scikit-learn, sympy, networkx, pillow, opencv-python, pyyaml, sqlalchemy, beautifulsoup4, lxml, regex, jinja2, astropy, biopython, and more.
- Manages the worker lifecycle (create, execute, terminate, dispose)
- Passes
packagesfromExecutionRequestto worker for pre-installation - Tracks Pyodide ready state — shows "loading" during initial interpreter download
- Worker is reused across executions (Pyodide persists state between runs)
- 60-second default execution timeout
A polymorphic runtime system that abstracts language differences behind a common interface.
type RuntimeType = 'javascript' | 'wasm' | 'python';
type RuntimeState = 'idle' | 'loading' | 'running' | 'error' | 'terminated';
interface ExecutionRequest {
code: string;
files?: Record<string, string>;
importMap?: Record<string, string>;
packages?: string[]; // Python packages to install before execution
}const AVAILABLE_RUNTIMES: Record<RuntimeType, RuntimeInfo> = {
javascript: { id: 'javascript', label: 'JavaScript', extensions: ['js','jsx','ts','tsx','mjs'], icon: '🟨', isLocal: true },
wasm: { id: 'wasm', label: 'WebAssembly', extensions: ['wat','wasm'], icon: '🟪', isLocal: true },
python: { id: 'python', label: 'Python', extensions: ['py'], icon: '🐍', isLocal: true },
};inferRuntime(filename) maps file extensions to RuntimeType.
Abstract base class providing:
- State machine —
setState()with pub/sub notification - Memory heartbeat — optional
memoryReportCallbackforwarding - Dispose pattern — terminates, clears listeners, sets state to
terminated
normalizeError(error, runtimeType) produces clean, user-friendly error messages:
- JavaScript — strips Worker/sandbox internal stack frames
- WASM — humanizes
CompileError,LinkError,RuntimeErrorprefixes - Python — removes Pyodide internal frames, remaps file references
The central orchestrator managing all execution:
Key Features:
- Instance caching and reuse (Python worker persists Pyodide across runs)
- All execution routes to local (remote is disabled by default)
- Gathers Python packages from DependencyManager and passes via
ExecutionRequest.packages - Stale-execution guards (monotonic
executionIdcounter) - Output streaming with resource guarding
- Error normalization
- VFS file provision to runtimes
- Performance profiling integration
Key Methods:
| Method | Description |
|---|---|
execute(code, runtimeType) |
Main entry point — initializes and executes |
terminate() |
Stops current execution on active runtime |
switchRuntime(type) |
Changes active runtime without executing |
setFileProvider(fn) |
Registers VFS file provider |
setDependencyManager(dm) |
Attaches package manager |
setPerformanceManager(pm) |
Attaches profiling |
onOutput(cb) |
Subscribe to execution output |
onStateChange(cb) |
Subscribe to runtime state changes |
onResourceUpdate(cb) |
Subscribe to resource monitoring |
dispose() |
Clean shutdown of all runtimes |
Tracks per-runtime resource usage in real-time:
Default Resource Limits:
| Limit | JavaScript | WASM | Python |
|---|---|---|---|
| Max execution time | 10s | 30s | 60s |
| Max memory | 128 MB | 256 MB | 512 MB |
| Max output messages | 10,000 | 10,000 | 10,000 |
| Max output bytes | 5 MB | 5 MB | 5 MB |
Monitoring cycle:
startTracking(runtimeId)— creates snapshot, starts 500ms interval timer- Worker heartbeats feed
recordMemory()for memory tracking - Output callbacks feed
recordOutput()for volume tracking - Each tick checks time/memory/output limits and fires
ResourceAlerton breach - Safety sweep auto-stops stale trackers after 5 minutes
createGuardedOutput() wraps the output callback:
- Records output volume per message
- Listens for
ResourceAlertevents → emits warning → callsonTerminate() - Prevents double-termination
CDN-backed package resolution, caching, and installation for JavaScript (esm.sh), Python (Pyodide/micropip), and WASM packages.
User adds package in DependencyPanel
│
▼
DependencyManager.addPackage(spec)
│
▼
Resolver.validate() → Resolver.resolve()
│
├── JavaScriptResolver → esm.sh CDN URL
├── PythonResolver → Pyodide builtin check → PyPI JSON API
└── WasmResolver → WASM package URL
│
▼
PackageCache (IndexedDB)
│
▼ (on execute)
RuntimeManager gathers packages
│
▼
Worker installs via loadPackage() or micropip.install()
- JavaScriptResolver — resolves via
esm.shCDN, generates import map entries - PythonResolver — classifies as Pyodide built-in or micropip package, validates against unsupported list (GPU libs, server frameworks, etc.), checks PyPI JSON API for pure-Python wheels
- WasmResolver — resolves WASM package URLs
| Method | Description |
|---|---|
addPackage(spec) |
Validate → resolve → cache → update manifest |
removePackage(runtime, name) |
Remove from manifest |
resolveAll() |
Re-resolve all pending/failed packages |
getJavaScriptImportMap() |
CDN URLs for JS worker import maps |
getPythonInstallList() |
{ builtins: string[], micropip: string[] } |
IndexedDB database: browserlab_packages (version 1)
High-resolution span-based profiling with categories: execution, compile, pyodide-load, package-install, cache-hit, cache-miss, network, vfs-operation, etc.
Features:
- Nested spans via
span.startChild() - Cold vs. hot start tracking
- Percentile analysis (p50, p95, p99)
- Max 2000 spans (auto-evicts oldest)
Two-tier caching:
- Tier 1: In-memory
Map— instant access - Tier 2: IndexedDB (
browserlab_perf_cache) — persisted across sessions
Eviction policies: LRU (default), LFU, TTL. Default: 100 MB, 500 entries, 24h TTL.
Time-series metrics with auto-generated performance hints:
| Trigger | Hint |
|---|---|
| Execution > 5s | Slow execution warning |
| Execution > 15s | Critical slow execution |
| Worker init > 3s | Slow worker initialization |
| Memory > 256MB | High memory usage |
| Memory > 512MB | Critical memory usage |
Intercepts network requests with three strategies:
| Strategy | Used For |
|---|---|
| Cache-first | Pyodide CDN, WASM files, Monaco chunks |
| Network-first | Frequently updated resources |
| Stale-while-revalidate | esm.sh, unpkg, skypack packages |
Cache name: browserlab-perf-v1, max entries: 200
main.tsx → Root.tsx
│
├── HomePage (landing page with feature cards)
│ │
│ └── "Open Editor" button → 350ms exit animation
│
└── VFSProvider → App.tsx (editor)
Landing page shown before the editor:
- Animated background grid with glowing orbs
- Gradient title with shimmer animation
- 6 feature cards (JavaScript, Python, WASM, VFS, Packages, Performance)
- "Open Editor" CTA button with smooth transition
- Developer credit footer
- Fully responsive
Editor layout wiring all hooks and panels:
- Auto-detects runtime from active file extension
- Global
Ctrl+Enterkeyboard shortcut for execution - Loading screen during initial VFS hydration
- Layout: sidebar (FileExplorer, DependencyPanel, PerformancePanel) + CodeEditor + ConsoleOutput
| Component | Description |
|---|---|
HomePage |
Landing page with animated hero and feature cards |
CodeEditor |
Monaco editor with language detection, dark theme |
ConsoleOutput |
Scrollable output panel with color-coded entries |
Toolbar |
Run/stop buttons, runtime selector, resource indicators |
FileExplorer |
VFS tree with create/rename/delete + drag-and-drop file upload from PC |
DependencyPanel |
Package search, add/remove, status indicators |
PerformancePanel |
4-tab dashboard: Health, Profiler, Cache, Hints |
The FileExplorer supports dragging files from your local machine directly onto the panel:
- Reads dropped files as text and creates them in the VFS
- Auto-selects the last dropped file in the editor
- Visual feedback: dashed border overlay with "Drop files here" message
- Works with any text file (
.js,.py,.json,.md, etc.)
Bridges RuntimeManager to React state:
- Creates singleton
RuntimeManager - Subscribes to output, state, resource, alert events
- Initializes
DependencyManager - Returns:
entries,isRunning,runtimeState,resourceSnapshot,depManager,execute(),clearConsole(),terminateRuntime(),setFileProvider()
Bridges PerformanceManager to React state:
- Periodic summary refresh every 5 seconds
- Returns:
perfManager,health,hints,summary,cacheStats,recentSpans,refreshSummary(),clearMetrics(),clearCache()
All execution is local by default:
const DEFAULT_DISPATCH_POLICY = {
remoteEnabled: false, // Cloud execution disabled
preferLocal: true, // Always prefer browser execution
fallbackToLocal: true,
confidenceThreshold: 0.6,
};- Target: ES2022
- Module: ESNext with bundler resolution
- Strict mode enabled
verbatimModuleSyntax: true- jsx: react-jsx
class VirtualFileSystem implements IFileSystem {
constructor(project: VFSProject)
getProject(): VFSProject
getNode(id: VFSNodeId): VFSNode | undefined
getChildren(parentId: VFSNodeId | null): VFSNode[]
createFile(parentId: VFSNodeId | null, name: string, content?: string): VFSNode
createDirectory(parentId: VFSNodeId | null, name: string): VFSNode
updateContent(id: VFSNodeId, content: string): void
renameNode(id: VFSNodeId, newName: string): void
deleteNode(id: VFSNodeId): void
getProjectFiles(): Record<string, string>
}class RuntimeManager {
readonly monitor: ResourceMonitor
setFileProvider(provider: () => Record<string, string>): void
setDependencyManager(manager: DependencyManager): void
setPerformanceManager(manager: PerformanceManager): void
execute(code: string, runtimeType: RuntimeType): Promise<void>
terminate(): void
switchRuntime(type: RuntimeType): void
onStateChange(cb: RuntimeStateChangeCallback): () => void
onOutput(cb: RuntimeOutputCallback): () => void
onResourceUpdate(cb: ResourceUpdateCallback): () => void
onResourceAlert(cb: ResourceAlertCallback): () => void
getState(type?: RuntimeType): RuntimeState
getActiveType(): RuntimeType
disposeRuntime(type: RuntimeType): void
dispose(): void
}class DependencyManager {
onEvent(cb: DependencyEventCallback): () => void
getManifest(): DependencyManifest
getPackagesForRuntime(runtime: RuntimeType): PackageRecord[]
addPackage(spec: PackageSpec): Promise<PackageRecord>
removePackage(runtime: RuntimeType, name: string): void
resolveAll(): Promise<void>
getJavaScriptImportMap(): Record<string, string>
getPythonInstallList(): { builtins: string[]; micropip: string[] }
dispose(): void
}class PerformanceManager {
readonly profiler: PerformanceProfiler
readonly cache: IntelligentCache
readonly metrics: MetricsCollector
initialize(): Promise<void>
startSpan(name, category, runtimeType?): ActiveSpan
measure<T>(name, category, runtimeType, fn): Promise<T>
getProfileSummary(): ProfileSummary
recordMetric(name, value, unit, tags?): void
getHealth(): SystemHealth
getHints(): readonly PerformanceHint[]
getCacheStats(): CacheStats
dispose(): void
}User clicks Run (or Ctrl+Enter)
│
▼
App.handleRun()
│
▼
useCodeExecution.execute(code, runtimeType)
│
▼
RuntimeManager.execute(code, runtimeType)
│
├── Gather Python packages from DependencyManager
│
├── getOrCreate(runtimeType)
│ │
│ ├── JavaScriptRuntime → sandbox.worker.ts → new Function()
│ ├── WasmRuntime → wat-compiler → WebAssembly.instantiate()
│ └── PythonRuntime → python.worker.ts → micropip.install() → Pyodide.runPythonAsync()
│
└── Output streams back to ConsoleOutput
User drags files from PC onto FileExplorer
│
▼
onDrop handler → FileReader.readAsText()
│
▼
VFS.createFile(null, filename, content)
│
▼
setActiveFile(newFileId) → Monaco editor shows file
| Store | Key Path | Purpose |
|---|---|---|
projects |
id |
VFS project state |
settings |
key |
Default project ID |
| Store | Key Path | Indexes | Purpose |
|---|---|---|---|
packages |
id |
runtime, expiresAt |
Cached package artifacts |
| Store | Key Path | Indexes | Purpose |
|---|---|---|---|
cache_entries |
key |
category, lastAccessedAt, accessCount |
Performance cache entries |
- Web Worker isolation — user code runs in dedicated workers, isolated from the main thread and DOM
- Blocked globals — dangerous APIs (
fetch,XMLHttpRequest,WebSocket,EventSource,indexedDB,caches,BroadcastChannel) blocked viaObject.defineProperty - Code execution via
new Function()— prevents access to worker's module scope - Timeout protection — configurable per runtime (5s JS, 10s WASM, 60s Python)
- Resource limits — memory, output volume, and execution time caps with auto-termination
- COOP/COEP headers —
Cross-Origin-Opener-Policy: same-originandCross-Origin-Embedder-Policy: credentiallessfor SharedArrayBuffer support (configured invercel.json)
- All persistence is local IndexedDB — no data leaves the browser
- No analytics, no telemetry
- Fully offline-capable after initial load
npm run build
# Output: dist/ directory with optimized assetsBuild stats: ~88 modules, ~2.4s build time
The project includes vercel.json with:
- Build command:
npm run build - Output directory:
dist - SPA routing: All non-asset paths rewrite to
index.html - COOP/COEP headers: Required for Pyodide/WASM SharedArrayBuffer support
- Credentialless COEP: Uses
credentiallessinstead ofrequire-corpso CDN resources (Pyodide, esm.sh) still load
{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"framework": "vite",
"rewrites": [
{ "source": "/((?!assets/).*)", "destination": "/index.html" }
],
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
{ "key": "Cross-Origin-Embedder-Policy", "value": "credentialless" }
]
}
]
}- Push to GitHub
- Import in Vercel — auto-detects Vite
- Click Deploy — that's it
Or via CLI:
npx vercel| Module Group | Count |
|---|---|
| VFS | 4 |
| Runtime (core + hybrid) | 14 |
| Dependencies | 8 |
| Performance | 7 |
| Components | 8 |
| Hooks | 2 |
| Workers | 2 |
| Context | 1 |
| Root files (main, Root, App, types) | 4 |
| Total | ~88 modules |
*BrowserLab — a 100% client-side browser IDE. Developed by Kautilya Reddy Racha & Dinesh Reddy Racha *