Gelectron is a non-commercial, community-driven open source project in early development. APIs may change. Not all features are implemented. Not affiliated with or endorsed by Electron or Mozilla.
Introduction
Gelectron is a drop-in replacement for Electron that uses the OS platform WebView (WKWebView / WebView2 / WebKitGTK) instead of Chromium, with a Rust core for native operations. The goal is to provide the same developer experience with smaller binaries and lower memory usage.
| Feature | Electron | Gelectron |
|---|---|---|
| Rendering Engine | Chromium | Platform WebView (WKWebView / WebView2 / WebKitGTK) |
| Language | C++ / Node.js | Rust / Node.js |
| API Compatibility | Native | Drop-in replacement (in progress) |
| Memory Usage | High (~586 MB total RSS) | Lower (~131 MB total RSS) |
| Node.js in Renderer | Yes | No (main process only) |
Prerequisites
- Rust — stable toolchain 1.75+ (rustup.rs)
- Node.js — 18 or newer
- npm — comes with Node.js
- Git — for cloning the repo
rustc --version # should be 1.75+
node --version # should be 18+
npm --version
Installation
git clone https://github.com/mileswolfallen2/gelectron.git
cd gelectron
npm install
cargo build --release -p gelectron
The first build will take a while — the Rust toolchain compiles tao and wry. Subsequent builds are much faster.
Quick Start
The fastest way to see Gelectron running:
cargo run --release -p gelectron -- demo/
This opens a window with an interactive demo app running HTML, CSS, JavaScript, canvas animations, and a live clock.
To run your own Electron app:
cargo run --release -p gelectron -- /path/to/your/electron-app
CLI Reference
| Command | Description |
|---|---|
gelectron <path-to-app> |
Run an Electron app |
gelectron <file.js> |
Run a main process script directly |
gelectron --version |
Print version |
gelectron --help |
Show help |
Node.js Fallback
If you don't want to build the Rust binary, the CLI can fall back to a pure Node.js shim:
node cli/gelectron.js /path/to/electron-app
In fallback mode no real window is created — only the JS API layer loads. Use the native binary for actual rendering.
Running Apps
Gelectron reads your app's package.json to find the main script, just like Electron does. Your existing Electron code should work without changes for basic apps.
{
"name": "my-app",
"main": "main.js"
}
const { app, BrowserWindow } = require('electron');
const path = require('path');
app.whenReady().then(() => {
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadFile(path.join(__dirname, 'index.html'));
});
app.on('window-all-closed', () => app.quit());
Then run it:
cargo run --release -p gelectron -- .
Demo App
The included demo showcases Gelectron's capabilities:
- Interactive counter (DOM updates via JS)
- Live clock driven by
requestAnimationFrame - Animated canvas with moving shapes
- CSS grid, gradients, transitions, and flexbox
demo/
├── package.json # { "main": "main.js" }
├── main.js # Creates BrowserWindow, loads index.html
└── index.html # HTML + CSS + JavaScript
Packaging for Distribution
Use gelectron-packager to build standalone executables:
# Install the packager
cd packager && npm link && cd ..
# Package for current platform
gelectron-packager --dir ./my-app --name MyApp
# Package for specific platforms
gelectron-packager --dir ./my-app --name MyApp --platform darwin --arch arm64
gelectron-packager --dir ./my-app --name MyApp --platform win32 --arch x64
gelectron-packager --dir ./my-app --name MyApp --platform linux --arch x64
The packager creates a self-contained distributable with the Rust binary, a bundled Node.js runtime (~20 MB), your app source, node_modules, and the Electron compatibility layer.
| Platform | Output |
|---|---|
| macOS | .app bundle (double-click to run) |
| Windows | Directory with .exe + .bat launcher |
| Linux | Directory with launcher script + .desktop file |
Every package also generates an update/ folder with a full-bundle .tar.gz and a latest.yml manifest (version, path, sha512). Upload both to a GitHub release and point the app's autoUpdater.setFeedURL() at latest.yml. Launcher scripts on macOS/Linux/Windows apply staged updates atomically before the app starts.
Main Process Modules
These modules are available via require('electron') in the main process.
| Module | Status | Notes |
|---|---|---|
app |
Full | All methods, properties, events, quit lifecycle, dock, badge, GPU, about panel, secure keyboard, window tracking |
ipcMain |
Full | handle(), handleOnce(), on(), once(), removeHandler(), full EventEmitter |
BrowserWindow |
Partial | create/show/hide/focus/min/max/close/destroy/setTitle/setSize/loadURL/loadFile. Missing: DevTools, navigation, capturePage, print, printToPDF |
Menu |
Partial | buildFromTemplate/append/insert/getMenuItemById, native setApplicationMenu via muda, _serialize. Missing: click events from native menu, role-based items |
MenuItem |
Partial | All properties (id/label/type/role/accelerator/enabled/visible/checked/submenu/click). Missing: role auto-behavior |
dialog |
Partial | showOpenDialog/showSaveDialog/showMessageBox/showErrorBox. Native dialogs not wired |
shell |
Partial | openExternal(), openPath() (Node mode). Missing: moveItemToTrash deletes permanently, shortcut stubs |
Notification |
Partial | API surface complete, show via browser Notification API if available. No native OS integration |
nativeImage |
Partial | createFromPath/Buffer/DataURL, toDataURL, getSize. Missing: resize/crop pixel transform, proper toPNG/toJPEG |
net |
Partial | fetch delegates to globalThis.fetch. Missing: net.request(), ClientRequest API |
process |
Partial | WebView mode polyfill: pid/argv/env/platform/versions/cwd/nextTick. Missing: memoryUsage/cpuUsage/uptime/kill |
webContents |
Partial | loadURL/loadFile/send/executeJavaScript/reload, session stub. Missing: navigation history, DevTools CDP, zoom, print, capturePage |
Tray |
Stub | API surface present, no native system tray |
safeStorage |
Stub | Base64 encoding, not real encryption |
autoUpdater |
Partial | Real implementation: setFeedURL/checkForUpdates/downloadUpdate/quitAndInstall/checkForAndNotifyIfAvailable, full event set, sha512-verified download, staged atomic apply on relaunch. Packaged apps only, requires a hosted latest.yml feed |
session |
Stub | defaultSession + fromPartition stubs, all no-ops |
clipboard |
Full | Full API: readText/writeText/readHTML/writeHTML/readRTF/writeRTF/readImage/writeImage/readBookmark/writeBookmark/readFindText/writeFindText/clear/availableFormats/has. Sync via Unix FIFO channel (macOS/Linux) or async bridge (Windows); RTF/bookmark/find-text/formats via NSPasteboard on macOS |
nativeTheme |
Partial | shouldUseDarkColors (getter + method), themeSource (getter/setter), shouldSystemUseDarkColors, queries Rust for system theme. Missing: theme change events |
screen |
Partial | getPrimaryDisplay/getAllDisplays/getDisplayMatching/getCursorScreenPoint/getMenuBarHeight via Rust bridge (tao). Missing: display event listeners |
systemPreferences |
Stub | Hardcoded dark mode / accent color |
powerMonitor |
Stub | getSystemIdleState returns 'active', no real monitoring |
globalShortcut |
Stub | register() always returns true, does nothing |
Renderer Process Modules
| Module | Status | Notes |
|---|---|---|
ipcRenderer |
Partial | invoke(), send(), sendSync(), on(), once(), removeListener(). Bridge not always wired, sendSync returns undefined |
contextBridge |
Partial | exposeInMainWorld with deep freeze. No real world isolation |
clipboard (renderer) |
Full | Same as main process: readText/writeText/readHTML/readImage/readRTF/readBookmark/readFindText/clear/availableFormats/has via bridge |
Gelectron's renderer is a clean browser context. There is no require(), no file system access, no native modules in the renderer. This is architecturally different from Electron. All Node.js code runs in the main process.
Common Modules
These modules are available in both main and renderer processes.
| Module | Status | Notes |
|---|---|---|
nativeImage |
Partial | createFromPath/Buffer/DataURL, toDataURL, getSize. Missing: resize/crop transform |
shell |
Partial | openExternal/openPath in Node mode |
Deprecated / Internal
| Module | Status | Notes |
|---|---|---|
BrowserView |
Missing | Deprecated, replaced by WebContentsView |
remote |
Missing | Removed in Electron 14+ |
webviewTag |
Missing | Deprecated, replaced by BrowserView/WebContentsView |
process (polyfill) |
Partial | WebView mode polyfill: pid/argv/env/platform/versions/cwd/nextTick. Missing: memoryUsage/cpuUsage/uptime/kill |
contextIsolation |
Stub | webPreferences.contextIsolation stored but no actual V8 isolate separation |
Missing Modules
These modules have not been started.
BaseWindow, ImageView, inAppPurchase, MessageChannelMain, powerSaveBlocker, ServiceWorkerMain, sharedTexture, View, WebContentsView, webFrameMain, webFrame, webUtils, navigation-history, parent-port, web-request, web-socket, window-open, local-ai-handler
How It Works
Gelectron has two execution paths:
1. Native Binary (recommended)
A standalone Rust binary using tao (windowing) and wry (WebView). It:
- Reads the target app's
package.jsonto find the main script - Generates a Node.js setup script that patches
require('electron')to point at Gelectron's JS compatibility layer - Spawns Node.js as a child process with piped stdin/stdout
- Runs a tao event loop with wry WebView windows
- Communicates with Node.js via JSON-line IPC
2. Node.js Fallback
When the native binary is not built, the CLI falls back to pure Node.js:
- Patches
Module._resolveFilenamesorequire('electron')resolves to Gelectron's shim - Loads the app's main script — the app runs against the JS compatibility layer
- No real window is created (API-only mode)
Project Structure
gelectron/
├── Cargo.toml # Rust workspace root
├── package.json # npm package
├── cli/
│ └── gelectron.js # CLI entry point (Node.js fallback)
├── src/electron/ # JS Electron compatibility layer
│ ├── index.js # Main exports
│ ├── app.js # app lifecycle
│ ├── browser-window.js # BrowserWindow + WebContents
│ ├── ipc-main.js # ipcMain
│ ├── ipc-renderer.js # ipcRenderer
│ ├── menu.js # Menu + MenuItem
│ ├── tray.js # Tray
│ ├── dialog.js # File/message dialogs
│ ├── shell.js # Shell operations
│ ├── notification.js # Notifications
│ ├── native-image.js # Image handling
│ ├── context-bridge.js # contextBridge
│ └── auto-updater.js # autoUpdater (real implementation)
├── crates/
│ ├── gelectron-core/ # N-API addon (Rust → Node.js)
│ └── gelectron-app/ # Standalone native binary
├── demo/ # Demo app
├── packager/ # gelectron-packager CLI
└── npm/darwin-arm64/ # Platform-specific npm packages
Environment Variables
| Variable | Description |
|---|---|
GELECTRON_DEV=1 |
Enable development mode |
GELECTRON_LOG=1 |
Enable verbose logging |
VITE_DEV_SERVER_URL=<url> |
Connect to a Vite dev server |
RUST_LOG=info |
Enable Rust-side logging |
Contributing
- Fork the repo
- Create a feature branch
- Make your changes
- Build and test:
cargo build --release -p gelectron - Test with the demo:
cargo run --release -p gelectron -- demo/ - Submit a PR
Known Limitations
- Servo not fully embedded — current WebView uses platform native (WebKit on macOS, WebView2 on Windows, webkit2gtk on Linux)
- No Node.js in renderer — all Node.js code runs in the main process only
- Auto-updater is packaged-app only — real checks/downloads/installs for packaged apps with a hosted feed; no-op in development and Node.js fallback mode
- Single-window only in the standalone binary
- Preload scripts are injected via WebView init scripts, not true Electron preload isolation
- Some APIs are stubs — see the API reference for details