add Gelectron devlog post detailing its development and advantages

This commit is contained in:
2026-07-28 01:11:39 -05:00
parent 237d922f6c
commit 00f1314925
2 changed files with 281 additions and 0 deletions
+275
View File
@@ -0,0 +1,275 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Why I Built Gelectron | Miles Wolf Allen</title>
<meta name="description" content="Why I built Gelectron — a Firefox-engine alternative to Electron. Memory usage, efficiency, and why bundling Chromium with every app is a problem.">
<link rel="canonical" href="https://milesallen.site/blog/gelectron-devlog.html">
<meta property="og:title" content="Why I Built Gelectron | Miles Wolf Allen">
<meta property="og:description" content="Why I built Gelectron — a Firefox-engine alternative to Electron. Memory usage, efficiency, and why bundling Chromium with every app is a problem.">
<meta property="og:image" content="https://milesallen.site/og.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Space+Mono:wght@400;700&family=Inter:wght@400;500;600;800&display=swap" rel="stylesheet">
<link rel="icon" type="image/svg+xml" href="../favicon.svg">
<link rel="stylesheet" href="../projects/style.css">
<style>
.article-header{padding:100px 0 40px;}
.article-eyebrow{font-family:var(--font-pixel);font-size:10px;color:var(--tape);margin-bottom:12px;text-transform:uppercase;letter-spacing:.1em;}
.article-date{font-family:var(--font-mono);font-size:12px;color:var(--muted);margin-bottom:8px;}
.article-header h1{font-family:var(--font-pixel);font-size:clamp(24px,5vw,44px);line-height:1.2;margin-bottom:16px;transform:rotate(-1deg);transform-origin:left center;text-shadow:3px 3px 0 var(--accent),-2px -2px 0 var(--tape);animation:glitch 5s ease-in-out infinite;}
@keyframes glitch{0%,90%,100%{text-shadow:3px 3px 0 var(--accent),-2px -2px 0 var(--tape);}92%{text-shadow:3px 3px 0 var(--red),-2px -2px 0 var(--accent);transform:rotate(-1deg) translateX(2px);}94%{text-shadow:3px 3px 0 var(--accent),-2px -2px 0 var(--red);transform:rotate(-1deg) translateX(-2px);}}
.article-tagline{font-size:16px;color:var(--muted);max-width:600px;line-height:1.7;font-family:var(--font-mono);margin-bottom:24px;}
.article-tags{display:flex;gap:8px;flex-wrap:wrap;}
.article-tag{font-family:var(--font-pixel);font-size:8px;padding:6px 12px;background:var(--accent);color:#000;text-transform:uppercase;}
.article-body{max-width:720px;}
.article-body h2{font-family:var(--font-pixel);font-size:16px;color:var(--accent);margin:40px 0 16px;text-transform:uppercase;}
.article-body h3{font-family:var(--font-mono);font-weight:700;font-size:16px;margin:32px 0 12px;color:var(--text);}
.article-body p{font-size:14px;color:#ccc;line-height:1.9;font-family:var(--font-mono);margin-bottom:16px;}
.article-body code{background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.12);padding:2px 6px;font-size:12px;font-family:var(--font-mono);}
.article-body blockquote{border-left:3px solid var(--accent);padding:12px 20px;margin:24px 0;background:rgba(255,255,255,0.02);font-style:italic;color:var(--muted);font-family:var(--font-mono);font-size:13px;}
.article-body ul{margin:12px 0 16px 20px;font-family:var(--font-mono);font-size:13px;color:#ccc;line-height:1.8;}
.article-body ul li{margin-bottom:6px;}
.article-body ul li::marker{color:var(--accent);}
.back-link{display:inline-flex;align-items:center;gap:8px;font-family:var(--font-mono);font-size:12px;color:var(--muted);margin-top:40px;text-decoration:none;transition:color .2s;}
.back-link:hover{color:var(--accent);}
</style>
</head>
<body>
<div class="cursor-dot" id="cursorDot"></div>
<div class="cursor-ring" id="cursorRing"></div>
<div class="progress-bar" id="progressBar"></div>
<button class="theme-toggle" id="themeToggle" aria-label="Toggle theme"></button>
<div class="shortcut-modal" id="shortcutModal">
<div class="shortcut-card">
<h3>Keyboard Shortcuts</h3>
<div class="shortcut-row"><span>Go Home</span><kbd>H</kbd></div>
<div class="shortcut-row"><span>Go to GitHub</span><kbd>G</kbd></div>
<div class="shortcut-row"><span>Toggle theme</span><kbd>T</kbd></div>
<div class="shortcut-row"><span>Show shortcuts</span><kbd>?</kbd></div>
<div class="close-hint">Press Esc or ? to close</div>
</div>
</div>
<div class="flash" id="flash"></div>
<div class="toast-stack" id="toastStack"></div>
<nav class="mobile-nav">
<a href="../index.html">Home</a>
<a href="../projects/fedl.html">FEDL</a>
<a href="../projects/omniemu.html">OmniEmu</a>
<a href="../projects/command-move.html">CommandMove</a>
<a href="https://github.com/mileswolfallen2" target="_blank">GitHub</a>
</nav>
<div class="container">
<nav>
<a href="../index.html" class="nav-logo"><span class="logo-dot"></span> milesallen</a>
<div class="nav-links">
<a href="../index.html#about">About</a>
<a href="../index.html#skills">Skills</a>
<a href="../index.html#projects">Projects</a>
<a href="../index.html#contact">Contact</a>
</div>
</nav>
<section class="article-header">
<div class="breadcrumb">
<a href="../index.html">Home</a>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
<a href="../index.html#blog">Devlog</a>
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg>
<span>Why I Built Gelectron</span>
</div>
<div class="article-eyebrow">// DEVLOG 005</div>
<div class="article-date">2026</div>
<h1>Why I Built Gelectron</h1>
<p class="article-tagline">Electron bundles Chromium with every app. That's 150-300MB per app, hundreds of MBs of RAM. I thought there had to be a better way.</p>
<div class="article-tags">
<span class="article-tag">Rust</span>
<span class="article-tag">Servo</span>
</div>
</section>
<section class="reveal">
<div class="article-body">
<h2>The Electron Problem</h2>
<p>Electron is everywhere. VS Code, Discord, Slack, Figma — some of the best desktop apps in the world are built with it. And for good reason: you write one web app, and it runs on macOS, Windows, and Linux. The developer experience is fantastic.</p>
<p>But there's a cost. Every single Electron app bundles its own copy of Chromium. That's not a shared system library — it's a full copy of Chrome, embedded inside your app. A simple todo app? 150MB on disk. A chat app? 200MB. A complex tool like VS Code? Over 300MB.</p>
<p>And it's not just disk space. Each Electron app spins up its own Chromium renderer process, its own GPU compositing pipeline, its own memory space. If you have three Electron apps open, you have three copies of Chromium running. On a laptop with 8GB of RAM, that adds up fast.</p>
<h2>The Memory Issue</h2>
<p>This is what bothered me the most. Memory usage in Electron apps is genuinely high. A minimal "hello world" Electron app uses 80-120MB of RAM. That's more than some full desktop applications. The reason is Chromium — it's a full web browser, and browsers are memory hogs.</p>
<p>Chromium keeps multiple processes running: the main process, renderer processes for each window, GPU processes, utility processes. Each one consumes memory. Even when your app is idle, Chromium is still managing tabs you don't have, networking you don't need, and features you'll never use.</p>
<p>On my Mac, I routinely have 5-6 apps open at once. If three of them are Electron-based, that's easily 1-1.5GB of RAM just for those apps. On a machine where I'm also running Docker, VS Code, and a browser with 30 tabs... something has to give.</p>
<h2>Why Not Just Use something else?</h2>
<p>There are alternatives to Electron. Tauri uses the system's webview instead of bundling Chromium, which solves the disk space problem. But system webviews are inconsistent — Safari's webview on macOS is different from WebView2 on Windows, which is different from whatever GTK ships on Linux. You lose consistency.</p>
<p>I wanted something different. I wanted the consistency of Electron — same rendering engine everywhere — but without the Chromium baggage. That's when I started looking at Servo.</p>
<h2>Enter Servo</h2>
<p>Servo is Mozilla's next-generation browser engine. It was designed from the ground up for embedding, parallelism, and modern hardware. It uses Stylo for CSS (Rust-powered, parallel style computation) and WebRender for GPU compositing (the same renderer Firefox uses).</p>
<p>The key insight is that Servo doesn't bundle a full browser. It's a rendering engine designed to be embedded in other applications. That's exactly what Electron does with Chromium, but Servo is built for this use case rather than having it bolted on.</p>
<p>And because it's written in Rust, you get memory safety without a garbage collector. No runtime overhead, no unexpected pauses, no memory leaks from C++ code. The Rust compiler catches whole categories of bugs that would be silent in C++.</p>
<h2>The Efficiency Argument</h2>
<p>Here's what got me excited. Servo's architecture is fundamentally more efficient than Chromium for the embedding use case:</p>
<ul>
<li><strong>Parallel CSS layout:</strong> Stylo computes styles in parallel across CPU cores. Chromium does this mostly single-threaded.</li>
<li><strong>GPU compositing via WebRender:</strong> The same renderer Firefox uses, designed for efficient GPU utilization from the start.</li>
<li><strong>No process-per-tab overhead:</strong> Servo doesn't spawn a new process for every rendering context. Less memory, less overhead.</li>
<li><strong>Smaller binary:</strong> Rust static linking produces smaller binaries than Chromium's C++ build system.</li>
<li><strong>Designed for embedding:</strong> Unlike Chromium, which had embedding support added later, Servo was built for this.</li>
</ul>
<p>I'm not claiming Gelectron is faster than Electron today — it is but . It's early, it's experimental, and many APIs are stubs. But the foundation is more efficient. The ceiling is higher. And that's what matters.</p>
<h2>How It Works</h2>
<p>Gelectron is a drop-in replacement for Electron. You run <code>gelectron .</code> instead of <code>electron .</code>, and your app should just work. Under the hood:</p>
<ul>
<li>A JavaScript compatibility layer maps Electron's API surface (BrowserWindow, ipcMain, Menu, Tray, etc.) to Gelectron's internals</li>
<li>A Rust N-API addon handles the heavy lifting: window management via winit, native menus via muda, system tray via tray-icon, file dialogs via rfd</li>
<li>Servo handles all rendering — HTML, CSS, JavaScript execution</li>
<li>Node.js runs the main process, just like Electron</li>
</ul>
<p>The goal is zero changes to existing Electron apps. Same <code>require('electron')</code> imports, same APIs, same code. Just a different engine underneath.</p>
<h2>Where It's At</h2>
<p>Let me be clear: Gelectron is very early. This is a proof of concept, not a production tool. Many Electron APIs are stubs. Some things break. Auto-updater doesn't exist yet. Multi-window support is limited.</p>
<p>But the core works. You can create windows, handle IPC, build menus, show dialogs. The foundation is there. And every week, more APIs get filled in.</p>
<h2>Why It Matters</h2>
<p>Desktop apps aren't going away. And the web platform keeps getting more powerful. The question is: how do we build cross-platform desktop apps without burning 300MB of disk and 200MB of RAM per app?</p>
<p>Gelectron is my answer to that question. It's early, it's rough, and it might not go anywhere. But if it works — if we can get Electron's developer experience with Servo's efficiency — that changes the economics of desktop app development.</p>
<p>Smaller apps. Less memory. Same code. That's the dream.</p>
<blockquote>The best infrastructure is the one you don't notice. If your app framework is using 200MB of RAM, you notice it.</blockquote>
<a href="../index.html#blog" class="back-link">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="14" height="14"><polyline points="15 18 9 12 15 6"/></svg>
Back to Devlog
</a>
</div>
</section>
<footer>
Built by Miles · 2026 · Powered by too much coffee and the Konami code
</footer>
</div>
<script>
fetch('https://api.github.com/repos/mileswolfallen2/')
.then(r => r.json())
.then(d => {})
.catch(() => {});
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/[email protected]/gsap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/[email protected]/ScrollTrigger.min.js"></script>
<script>
gsap.registerPlugin(ScrollTrigger);
const heroTl = gsap.timeline({ defaults: { ease: 'power3.out' } });
heroTl
.from('.breadcrumb', { x: -30, opacity: 0, duration: 0.6 })
.from('.article-header h1', { y: 50, opacity: 0, duration: 0.8 }, '-=0.3')
.from('.article-tagline', { y: 30, opacity: 0, duration: 0.7 }, '-=0.4')
.from('.article-tag', { y: 20, opacity: 0, scale: 0.8, duration: 0.4, stagger: 0.06 }, '-=0.3');
gsap.from('nav', { y: -20, opacity: 0, duration: 0.6, ease: 'power2.out' });
document.querySelectorAll('.article-body h2').forEach(title => {
gsap.from(title, { scrollTrigger: { trigger: title, start: 'top 85%' }, y: 30, opacity: 0, duration: 0.7, ease: 'power3.out' });
});
document.querySelectorAll('.article-body p, .article-body ul, .article-body blockquote').forEach(el => {
gsap.from(el, { scrollTrigger: { trigger: el, start: 'top 88%' }, y: 20, opacity: 0, duration: 0.6, ease: 'power3.out' });
});
gsap.from('footer', { scrollTrigger: { trigger: 'footer', start: 'top 95%' }, opacity: 0, duration: 0.8 });
window.addEventListener('load', () => ScrollTrigger.refresh());
</script>
<script>
var toastQueue=document.getElementById('toastStack');var shownToasts={};
function showToast(id,title,body,icon){
if(shownToasts[id])return;shownToasts[id]=true;
var t=document.createElement('div');t.className='toast';
t.innerHTML='<span class="icon">'+(icon||'!')+'</span><span><span class="title">'+title+'</span>'+body+'</span>';
toastQueue.appendChild(t);
gsap.to(t,{x:0,duration:.6,ease:'power3.out'});
setTimeout(function(){gsap.to(t,{x:'-150%',opacity:0,duration:.4,ease:'power2.in',onComplete:function(){t.remove();}});},3000);
}
(function(){
var seq=['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a'];
var pos=0;
window.addEventListener('keydown',function(e){
var key=e.key.length===1?e.key.toLowerCase():e.key;
if(key===seq[pos]){pos++;if(pos===seq.length){pos=0;triggerKonami();}}
else{pos=(key===seq[0])?1:0;}
});
function triggerKonami(){
showToast('konami','SECRET FOUND','Konami Code Activated','*');
var flash=document.getElementById('flash');
gsap.fromTo(flash,{opacity:.5},{opacity:0,duration:.5});
gsap.to('body',{x:'random(-5,5)',y:'random(-5,5)',duration:.05,repeat:10,ease:'sine.inOut',onComplete:function(){gsap.set('body',{x:0,y:0});}});
document.body.style.cursor='auto';
var style=document.createElement('style');style.id='konami-cursor';
style.textContent='*{cursor:auto !important;}';
document.head.appendChild(style);
}
})();
</script>
<script>
(function(){
var dot=document.getElementById('cursorDot');
var ring=document.getElementById('cursorRing');
if(!dot||!ring)return;
var mx=0,my=0,rx=0,ry=0;
document.addEventListener('mousemove',function(e){mx=e.clientX;my=e.clientY;dot.style.left=mx+'px';dot.style.top=my+'px';});
(function loop(){
rx+=(mx-rx)*0.15;ry+=(my-ry)*0.15;
ring.style.left=rx+'px';ring.style.top=ry+'px';
requestAnimationFrame(loop);
})();
document.querySelectorAll('a,button,.meta-chip,.tech-badge,.feature-card').forEach(function(el){
el.addEventListener('mouseenter',function(){dot.classList.add('hover');ring.classList.add('hover');});
el.addEventListener('mouseleave',function(){dot.classList.remove('hover');ring.classList.remove('hover');});
});
})();
</script>
<script>
(function(){
var bar=document.getElementById('progressBar');
window.addEventListener('scroll',function(){
var pct=window.scrollY/(document.body.scrollHeight-window.innerHeight)*100;
bar.style.width=pct+'%';
},{passive:true});
})();
(function(){
var btn=document.getElementById('themeToggle');
var saved=localStorage.getItem('theme');
if(saved==='light')document.body.classList.add('light');
function updateIcon(){btn.textContent=document.body.classList.contains('light')?'\u263E':'\u2600';}
updateIcon();
btn.addEventListener('click',function(){
document.body.classList.toggle('light');
localStorage.setItem('theme',document.body.classList.contains('light')?'light':'dark');
updateIcon();
});
})();
(function(){
var modal=document.getElementById('shortcutModal');
function toggleModal(){modal.classList.toggle('open');}
window.addEventListener('keydown',function(e){
if(e.key==='?'){e.preventDefault();toggleModal();}
if(e.key==='Escape')modal.classList.remove('open');
if(e.key==='t'||e.key==='T'){
if(document.activeElement&&document.activeElement.tagName==='INPUT')return;
document.body.classList.toggle('light');
localStorage.setItem('theme',document.body.classList.contains('light')?'light':'dark');
var btn=document.getElementById('themeToggle');
btn.textContent=document.body.classList.contains('light')?'\u263E':'\u2600';
}
if(e.key==='h'||e.key==='H')window.location.href='../index.html';
if(e.key==='g'||e.key==='G')window.location.href='https://github.com/mileswolfallen2';
});
modal.addEventListener('click',function(e){if(e.target===modal)modal.classList.remove('open');});
})();
</script>
</body>
</html>
+6
View File
@@ -753,6 +753,12 @@
<h2 class="section-title reveal">Devlog</h2>
<p class="section-sub reveal">things I've learned, broken, and fixed</p>
<div class="blog-grid">
<a class="blog-card reveal" href="blog/gelectron-devlog.html">
<div class="blog-date">2026</div>
<div class="blog-title">Why I Built Gelectron</div>
<div class="blog-excerpt">Electron bundles Chromium with every app. 150-300MB per app, hundreds of MBs of RAM. There had to be a better way.</div>
<div class="blog-tags"><span class="blog-tag">Rust</span><span class="blog-tag">Servo</span></div>
</a>
<a class="blog-card reveal" href="blog/omniemu-devlog.html">
<div class="blog-date">2026</div>
<div class="blog-title">Why I Built OmniEmu</div>