Files
bigjakk 17e5c415c6 Update README with Linux x64 download
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 14:59:53 +00:00

190 lines
6.4 KiB
Markdown

# Electron WebSocket Fix
Patched Electron builds that fix a Chromium regression where continuous mouse input starves WebSocket and Worker message dispatch when `--disable-frame-rate-limit` is active.
This is critical for competitive browser-based games (like [Krunker](https://krunker.io)) running in Electron, where shooting (holding left click + moving mouse) causes network freezes of 100-300ms+.
## Downloads
Pre-built patched binaries are available on the [Releases page](https://gitea.crjlab.net/bigjakk/Electron-Websocket-Fix/releases):
### Windows x64
| File | Electron Version | Size |
|------|-----------------|------|
| `electron-v40.6.1-release-patched-win32-x64.zip` | v40.6.1 (latest stable) | 133MB |
| `electron-v42.0.0-nightly-release-patched-win32-x64.zip` | v42.0.0-nightly | 137MB |
### Linux x64
| File | Electron Version | Size |
|------|-----------------|------|
| `electron-v43.0.0-nightly-release-patched-linux-x64.zip` | v43.0.0-nightly | 113MB |
All builds are full release builds (`is_official_build = true`) with maximum optimizations. Additional versions can be built from source -- see [`BUILD-GUIDE.md`](BUILD-GUIDE.md).
## The Problem
When an Electron app uses these flags (common for competitive gaming):
```javascript
app.commandLine.appendSwitch('disable-frame-rate-limit');
app.commandLine.appendSwitch('disable-gpu-vsync');
```
...continuous mouse input with left-click held down causes WebSocket `onmessage` callbacks to be delayed by 100-300ms+. This manifests as network "freezing" -- player positions stop updating, hit registration breaks, and the game becomes unplayable during gunfights.
### Root Cause
Three factors in Chromium's Blink main thread scheduler combine to create the issue:
1. **Input tasks at `kHighestPriority`** (priority level 1) while WebSocket/Worker tasks sit at `kNormalPriority` (level 7)
2. **No cross-priority anti-starvation** in `task_queue_selector.cc` -- it always picks from the highest active priority queue, with no mechanism to let lower-priority tasks run
3. **Compositor priority boost during input** -- when mouse is held + moving (`UseCase::kMainThreadCustomInputHandling`), the compositor queue gets boosted to `kHighestPriority`. With `--disable-frame-rate-limit`, the `BackToBackBeginFrameSource` posts `SEND_BEGIN_MAIN_FRAME` tasks at zero delay, creating an infinite loop of highest-priority tasks that permanently starves everything else
This regression was introduced in Chromium 84 when `PrioritizeCompositingAfterInput` was made unconditional ([CL 2132022](https://chromium-review.googlesource.com/c/chromium/src/+/2132022)).
## The Fix
Two changes in one file (`main_thread_scheduler_impl.cc`):
1. **Lower input task priority** from `kHighestPriority` to `kNormalPriority`
2. **Cap compositor priority** to `kNormalPriority` via `std::max()`
See [`patches/ws-priority-patch.diff`](patches/ws-priority-patch.diff) for the exact diff.
### Test Results
12-second automated stress test with continuous CDP mouse input (left-click held, circular movement):
| Build | p99 Latency | Max Latency | Messages >50ms | Mouse Events | Frames |
|-------|-------------|-------------|----------------|-------------|--------|
| **Unpatched** | ~97ms | ~308ms | 8.6% | ~11,380 | ~6,300 |
| **Patched** | ~34ms | ~38ms | **0%** | ~12,360 | ~7,620 |
The patch not only eliminates starvation but actually **improves** both input throughput (+9% mouse events) and frame rate (+21% frames) because it prevents the input/compositor priority cascade from monopolizing the main thread.
## Usage
### Option A: Direct Binary
Extract the zip and run your app:
**Windows:**
```bash
electron.exe path/to/your/app
```
**Linux:**
```bash
./electron path/to/your/app
```
### Option B: Replace in node_modules
```bash
# Back up original
mv node_modules/electron/dist node_modules/electron/dist-original
# Extract patched version
mkdir node_modules/electron/dist
cd node_modules/electron/dist
unzip path/to/electron-v40.6.1-release-patched-win32-x64.zip
```
### Option C: electron-builder
In `package.json`:
```json
{
"build": {
"electronDist": "path/to/extracted/dist",
"electronVersion": "40.6.1"
}
}
```
### Option D: electron-forge
In `forge.config.js`:
```js
module.exports = {
packagerConfig: {
electronZipDir: 'path/to/extracted/dist'
}
};
```
## Verification
An automated stress test is included in the [`test/`](test/) directory:
```bash
cd test
npm install
# Windows
path/to/patched/electron.exe cdp-test.js 8085 PATCHED
# Linux
path/to/patched/electron cdp-test.js 8085 PATCHED
```
The test uses CDP `Input.dispatchMouseEvent` to simulate continuous mouse input (the only reliable automated method -- Electron's `sendInputEvent` API bypasses the compositor thread and doesn't trigger the bug).
Expected output for a patched build: **0% of WebSocket messages >50ms**.
## Building From Source
Full build instructions are in [`BUILD-GUIDE.md`](BUILD-GUIDE.md).
Quick summary:
```bash
# 1. Set up environment (depot_tools, build toolchain, Python, etc.)
# 2. Initialize and sync Electron source
e init --root=$HOME/electron my-build --import release # Linux
e init --root=C:\electron my-build --import release # Windows
e sync
# 3. Check out desired version
cd src/electron && git checkout v40.6.1
cd .. && gclient sync --with_branch_heads --with_tags
# 4. Apply patch
git apply path/to/ws-priority-patch.diff
# 5. Configure and build
mkdir -p out/Release
# Set out/Release/args.gn:
# import("//electron/build/args/release.gn")
# is_official_build = true
# use_remoteexec = false
# use_reclient = false
buildtools/linux64/gn gen out/Release # Linux
buildtools/win/gn.exe gen out/Release # Windows
ninja -C out/Release electron
ninja -C out/Release electron:electron_dist_zip
```
Expect 6-10+ hours for a full build on a modern machine (24 cores, 64GB RAM).
## Patch Details
The patch modifies Chromium source (not Electron source), so it applies to any Electron version since Electron 10 (Chromium 84+) on any platform (Windows, Linux, macOS). Line numbers may shift between versions but the function names remain the same:
- `ComputePriority()` -- search for `PrioritisationType::kInput`
- `ComputeCompositorPriority()` -- search for that function name
File: `third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.cc`
## License
The patch itself is provided as-is. Electron is MIT licensed. Chromium is BSD licensed.