An abstraction layer for Electron that provides a clean, secure API for managing playboxed applications, child processes, and navigation.
Electron Playbox follows "The 9 Commandments" - a minimal, focused API surface that handles the most common Electron framework needs:
The 7 Core Commandments:
- Clear Playbox - Reset your playbox environment
- Prepare Playbox - Set up folder structure from config
- Assemble Playbox - Build files from components
- Start App - Launch external processes or Node.js apps
- Kill App - Terminate running processes
- List Apps - View active child processes
- Navigate - Change window location safely
Plus 2 Additional Commandments:
- Read App - Read buffered output from child processes
- End Session - Clear playbox and quit application
- π Secure by default - Path validation prevents directory traversal
- π¦ Playbox management - Component-based file assembly system
- π― Process control - Spawn and manage child processes (
.exe,.js) - π§ Safe navigation - Custom protocol with validated routing
- π Process monitoring - Read stdout/stderr from child processes
- π Built-in logging - Automatic process and error logs
- π§Ή Clean shutdown - Automatic child process cleanup
npm install// In your renderer process (HTML/JS)
// Navigate to a different page (defaults to dynamic://)
await window.api.navigate({ urlPath: "playbox/game.html" });
// Or navigate to static bundled content
await window.api.navigate({ urlPath: "launcher/menu.html", protocol: "static" });
// Clear the entire playbox
const result = await window.api.clearPlaybox();
// Prepare playbox from config
await window.api.preparePlaybox("myapp.json");
await window.api.assemblePlaybox("myapp.json");
// Launch an application (defaults to static://)
const { data } = await window.api.startApp({ appPath: "apps/game.exe" });
console.log(`Started PID: ${data.pid}`);
// Launch from dynamic:// (userData) if needed
await window.api.startApp({ appPath: "playbox/server.js", protocol: "dynamic" });
// Read process output
const output = await window.api.readApp({ pid: data.pid });
if (output.success) {
const text = atob(output.data.stdout);
console.log("Process output:", text);
}
// Check running apps
const { data: apps } = await window.api.listApps();
console.log(`Running: ${apps.count} processes`);
// Kill a specific app
await window.api.killApp(data.pid);
// End the session (clear playbox and quit)
await window.api.endSession();Configs live in frontend/configs/ and define how to build your playbox:
{
"apps": [
{
"output": "apps/game.html",
"components": ["header.html", "game-canvas.html", "footer.html"],
"componentPath": "game"
}
],
"scripts": [
{
"output": "game.js",
"components": ["engine.js", "physics.js", "renderer.js"],
"componentPath": "game"
},
{
"output": "config.json",
"components": ["settings.json"]
}
]
}Assembly behavior is automatic:
- 0 components β creates empty file
- 1 component β copies file as-is
- 2+ components β concatenates files together
All API methods return a consistent response format:
{
success: true, // or false
data: { ... }, // on success
message: "error" // on failure
}clearPlaybox(folder?)- Clear playbox or specific folderpreparePlaybox(configPath)- Create folder structure from configassemblePlaybox(configPath)- Build files from components
startApp(args)- Launch .exe or .js fileskillApp(pid)- Terminate a specific processlistApps()- Get all running child processesreadApp(pid)- Read buffered stdout/stderr from a process
navigate(args)- Load a new page in the main window
endSession()- Clear playbox and quit application
See API.md for complete documentation with examples.
electron-playbox/
βββ main.js # Main process entry point
βββ preload.js # API bridge (contextBridge)
βββ frontend/ # Served via static:// protocol
β βββ components/ # Reusable HTML/JS/CSS components
β βββ configs/ # Playbox assembly configs
β βββ launcher/ # Launcher UI files
βββ localModules/
β βββ appProtocol.js # Custom protocol handlers (static/dynamic)
β βββ basePath.js # Path resolution
β βββ loggers.js # File logging
β βββ playboxHelpers.js # Config validation
β βββ commandments/ # The 9 commandments
β βββ playbox.js # Clear, prepare, assemble
β βββ processControl.js # Start, kill, list, read
β βββ navigation.js # Navigate
β βββ endSession.js # End session
βββ [userData]/ # Served via dynamic:// protocol
βββ playbox/ # Runtime assembly target (ephemeral)
βββ logs/ # Application logs
βββ [any other data]/ # Persistent user/app data
Electron Playbox uses two custom protocols for serving content:
- Read-only bundled application files
- Source for components to be assembled
- Launcher UI and configs
- Example:
static://launcher/menu.html
- Writable runtime content
- Playbox assemblies
- User data and settings
- Any persistent application data
- Example:
dynamic://playbox/game/index.html
Key Insight: The playbox directory (userData/playbox/) is where assembled applications are deployed. It's ephemeral (cleared by endSession()), while other userData content persists.
# Development
npm run raw
# Package for your platform
npm run package-windows --appname=MyApp
npm run package-linux --appname=MyApp
npm run package-mac-x64 --appname=MyApp
npm run package-mac-arm64 --appname=MyApp- All paths are validated against directory traversal attacks
static://serves only files withinfrontend/(install directory)dynamic://serves only files withinuserData/(app data directory)- Playbox content lives in
userData/playbox/with full read/write - Child processes can be spawned from either
static://ordynamic://paths - Context isolation enabled, node integration disabled in renderer
- IPC surface is capability-based (only exposed methods available)
- Important: Child processes spawned with
.jsextension run with full Node.js system access viaELECTRON_RUN_AS_NODE=1- validate what you launch - Important: Process output via
readApp()is base64-encoded for safe IPC transmission
The assembly system allows building different application configurations from a shared component library:
Use Cases:
- Different feature sets per user tier (basic vs professional)
- Multiple applications sharing common components
- A/B testing different UI configurations
- Educational platforms with progressive feature unlocking
- Multi-tenant applications with per-tenant customization
Example Workflow:
// Educational platform: beginner mode
{
"ide": [{
"output": "editor.js",
"components": ["core.js", "simple-debugger.js", "help.js"]
}]
}
// Professional mode
{
"ide": [{
"output": "editor.js",
"components": ["core.js", "advanced-debugger.js", "profiler.js", "git.js"]
}]
}Same components, different assemblies. The config determines what gets built.
Two log files are created in the application root directory:
process.log- General debug info and process lifecycleerror.log- Error messages and failures
Logs are cleared on each app start. All child process stdout/stderr is also logged here in addition to being buffered for readApp().
What This System Actually Does:
Electron Playbox is a platform for building and running desktop applications with:
- Component-based architecture - Build apps from reusable components
- Multi-app runtime - Multiple applications in one Electron window
- System integration - Child process spawning for native tools
- Persistent + ephemeral storage - userData for data, playbox for runtime
- Dynamic assembly - Apps assembled on-demand from configurations
Real-World Applications:
- Development tool suites (IDE, debugger, profiler)
- Enterprise application platforms (different apps per user role)
- Educational platforms (different environments per course level)
- Gaming platforms (launcher + games sharing engine components)
- Creative suites (multiple tools sharing rendering components)
It's not a kiosk system - there's nothing preventing you from building VSCode-scale applications here. The playbox is simply the deployment target for your assembled frontend code, while the rest of userData can hold project files, settings, extensions, etc.
MIT
TSTEAS2008