Win32's window model — message loop, msgProc, sendMessage — running natively in the browser. Reactive bindings included, no build step required.
<script src="https://cdn.jsdelivr.net/gh/quellabs/wakapac@main/wakapac.min.js"></script>
wakaPAC('#hero-demo', {
_rx: 0.4, _ry: 0.0,
msgProc(event) {
switch (event.message) {
case wakaPAC.MSG_WEBGL_READY: {
const gl = event.detail.glContext;
this._prog = buildShaders(gl); // vertex + fragment shader
this._geo = uploadCube(gl); // 8 vertices, index buffer
gl.enable(gl.DEPTH_TEST);
wakaPAC.releaseDC(gl);
break;
}
case wakaPAC.MSG_PAINT: {
const gl = wakaPAC.getDC(this.pacId);
const size = wakaPAC.getCanvasSize(this.pacId);
this._rx += 0.003;
this._ry += 0.007;
gl.viewport(0, 0, size.width, size.height);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
drawCube(gl, this._prog, this._geo, this._rx, this._ry, size);
wakaPAC.releaseDC(gl);
break;
}
case wakaPAC.MSG_SIZE: {
const w = wakaPAC.LOWORD(event.lParam);
const h = wakaPAC.HIWORD(event.lParam);
wakaPAC.resizeCanvas(this.pacId, w, h);
break;
}
}
}
}, {
dcAttributes: { antialias: true, depth: true },
renderLoop: true
});
// Full source incl. shaders: github.com/quellabs/wakapac/examples/example31.html
Win32's window architecture, directly in the browser. If you know WndProc, you know msgProc.
Components communicate via sendMessage and postMessage. One data path. Predictable by construction.
State, computed properties, two-way bindings — all there, zero build step, zero dependencies.
Copy, paste, done. No installation. No configuration.
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/gh/quellabs/wakapac@main/wakapac.min.js"></script>
</head>
<body>
<div id="app">
<h1>Hello {{name}}!</h1>
<input data-pac-bind="value: name">
</div>
<script>
wakaPAC('#app', {
name: 'World'
});
</script>
</body>
</html>
That's a complete WakaPAC component • ~95KB minified • MIT licensed
Not just reactive bindings — a complete application architecture.
One function. Every message. Mouse, keyboard, resize, focus, clipboard — all in a single switch. WakaPAC's WndProc.
Components talk exactly as Win32 windows do. Synchronous or queued delivery. Custom messages offset from MSG_USER.
Map "Ctrl+S" to a command ID. MSG_ACCEL delivers it to msgProc. Child tables override parents; parent tables cover all descendants.
Mouse gestures, drag-and-drop with file metadata, synchronous clipboard interception — all arriving through the same msgProc switch.
getDC() on a canvas component returns its drawing context with Win32-style acquire/release. 2D and 3D through WebGL. HiDPI handled internally.
State, computed properties, two-way bindings, list rendering. Write this.items.push(...) and the DOM updates. No build step, no dependencies.
Win32's window model is a complete application architecture — message loop, WndProc, SendMessage, device contexts. It works. I wanted the same model in the browser, with reactivity built in. Not another binding library. An application model.
— Floris, Quellabs
Real code, real patterns, ready to copy
wakaPAC('#box', {
msgProc(event) {
switch (event.message) {
case wakaPAC.MSG_LBUTTONDOWN: {
wakaPAC.setCapture(this.pacId);
this._dragging = true;
break;
}
case wakaPAC.MSG_MOUSEMOVE:
if (this._dragging) {
...
}
break;
case wakaPAC.MSG_LBUTTONUP:
wakaPAC.releaseCapture();
this._dragging = false;
break;
case wakaPAC.MSG_CAPTURECHANGED:
this._dragging = false;
break;
}
}
});
setCapture() routes all mouse events to this component — even when the cursor leaves it
const MSG_REFRESH = wakaPAC.MSG_USER + 1;
const MSG_RESET = wakaPAC.MSG_USER + 2;
// Sender
wakaPAC('#toolbar', {
refresh() {
wakaPAC.sendMessage('content', MSG_REFRESH, 0, 0);
},
reset() {
wakaPAC.postMessage('content', MSG_RESET, 0, 0);
}
});
// Receiver
wakaPAC('#content', {
msgProc(event) {
switch (event.message) {
case MSG_REFRESH: this.reload(); break;
case MSG_RESET: this.clear(); break;
}
}
});
sendMessage() waits for the receiver to return. postMessage() queues and returns immediately.
wakaPAC('#app', {
firstName: 'John',
lastName: 'Doe',
items: [
{ name: 'Apple', price: 10 },
{ name: 'Banana', price: 20 }
],
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`;
},
totalPrice() {
return this.items.reduce(
(sum, item) => sum + item.price, 0
);
}
}
});
Recalculates automatically when dependencies change
<div id="app">
<p data-pac-bind="visible: !browserOnline">
⚠️ You are offline
</p>
<p>
Viewport: {{browserViewportWidth}} × {{browserViewportHeight}}
</p>
<p data-pac-bind="visible: !browserVisible">
Tab hidden — updates paused
</p>
<p>Scrolled: {{browserScrollY}}px</p>
<p>Connection: {{browserNetworkQuality}}</p>
</div>
Network, viewport, scroll, visibility — all reactive, zero setup
The same architecture Win32 uses — and the reason WakaPAC behaves like a desktop application framework rather than a binding library.
Your HTML template. Owns its visual area and renders state — but never reads or writes state directly. The equivalent of a Win32 window surface.
Your data and business logic. Knows nothing about the DOM. Never triggers a UI update directly — it only holds state. The equivalent of application data in a Win32 program.
WakaPAC itself, and your msgProc. Mediates all communication between Presentation and Abstraction — state changes flow here, and the DOM follows.
Why this matters: Presentation and Abstraction never communicate directly — a template cannot call your business logic, and state cannot reach the DOM except through Control. There is no way for a template to reach into your data, and no way for your data to reach the DOM on its own. That unidirectional boundary is what makes WakaPAC applications predictable in the same way Win32 applications are predictable.
A family of open-source tools built around the same philosophy.
Download one file and write working code in the next 60 seconds
Open source • MIT licensed • ~95KB minified • Zero dependencies • Sponsor this project