Introduction & PAC Pattern
What is WakaPAC?
WakaPAC is a reactive UI library in a single JavaScript file that provides data binding, component coordination, and automatic DOM updates.
About the name
The name WakaPAC combines Waka (inspired by Pac-Man) with PAC, the Presentation-Abstraction-Control architecture pattern it implements.
Quick Example
Here's a complete interactive application:
<!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" placeholder="Enter your name">
<p>Character count: {{ name.length }}</p>
</div>
<script>
wakaPAC('#app', {
name: 'World'
});
</script>
</body>
</html>
That's it. Type in the input and watch the heading and character count update automatically. No compilation, no virtual DOM, no JSX - just reactive HTML.
Key Features
Reactive Data Binding
Two-way synchronization between application state and the DOM. Updating your data automatically updates the UI, and user interaction updates state without manual event wiring.
<input data-pac-bind="value: email">
<p>You entered: {{ email }}</p>
Declarative Templates
Declare UI behavior directly in HTML using bindings and expressions. Templates describe how data maps to the DOM without imperative DOM manipulation.
<button data-pac-bind="click: save, enable: !saving">
{{ saving ? 'Saving...' : 'Save' }}
</button>
<ul data-pac-bind="foreach: items" data-pac-item="item">
<li>{{ item.name }}: {{ item.price }}</li>
</ul>
Computed Properties
Define derived values that stay in sync automatically. Computed properties recalculate whenever their dependencies change, without manual bookkeeping.
wakaPAC('#app', {
firstName: 'John',
lastName: 'Doe',
computed: {
fullName() {
return `${this.firstName} ${this.lastName}`;
}
}
});
Component Hierarchy
Coordinate parent and child components through built-in messaging. Components can exchange events and data without manual wiring or shared global state.
// Parent component
wakaPAC('#parent', {
receiveFromChild(type, data) {
console.log('Child sent:', type, data);
}
});
// Child component - automatically detects parent
wakaPAC('#child', {
sendUpdate() {
this.notifyParent('update', { value: 42 });
}
});
Multiple Element Binding
Instantiate reusable components across multiple DOM elements. Each matched element becomes an independent reactive instance with its own state.
<div class="counter">Count: {{count}} <button data-pac-bind="click: increment">+</button></div>
<div class="counter">Count: {{count}} <button data-pac-bind="click: increment">+</button></div>
<script>
wakaPAC('.counter', { count: 0, increment() { this.count++; } });
</script>
Browser & Container Properties
Access browser and container state as reactive data. Scroll position, visibility, dimensions, and connectivity are exposed as properties that update automatically without manual listeners.
<button data-pac-bind="visible: browserScrollY > 300, click: scrollToTop">
↑ Back to Top
</button>
<div data-pac-bind="visible: !browserOnline" class="offline-banner">
You're offline - changes will sync when reconnected
</div>
Expression Language
Use expressive inline logic directly in templates. Bindings support conditional logic, arithmetic, and common data operations without external helpers. Expressions are evaluated through a dedicated parser for predictable execution.
<p>Total: ${{ total }}</p>
<span>{{ count > 0 ? count + ' items' : 'Empty' }}</span>
<div data-pac-bind="class: { active: isSelected, disabled: !enabled }">
Desktop Subsystem
Handle interaction through a unified message-driven event system. Browser input and system events flow through a single interface for consistent processing.
wakaPAC('#app', {
msgProc(event) {
switch (event.message) {
case wakaPAC.MSG_LBUTTONDOWN:
// wParam contains modifier flags: MK_SHIFT, MK_CONTROL, etc.
// lParam contains mouse coordinates
const x = wakaPAC.LOWORD(event.lParam);
const y = wakaPAC.HIWORD(event.lParam);
console.log(`Left click at ${x},${y}`);
break;
case wakaPAC.MSG_KEYDOWN:
// wParam is the virtual key code
if (event.wParam === wakaPAC.VK_RETURN) {
console.log('Enter pressed');
}
// Check modifiers in lParam
if (event.lParam & wakaPAC.KM_CONTROL) {
console.log('Ctrl was held');
}
break;
}
}
});
Complete feature set:
- Message constants: MSG_LBUTTONDOWN, MSG_KEYDOWN, MSG_MOUSEMOVE, MSG_TIMER, MSG_USER + all Win32 message types
- Virtual key codes: VK_RETURN, VK_ESCAPE, VK_F1-F12, VK_A-Z + complete Win32 VK_* constants
- Modifier flags: MK_SHIFT, MK_CONTROL, MK_ALT, KM_SHIFT, KM_CONTROL, KM_ALT
- Message functions: sendMessage(), postMessage() for inter-component communication
- Hit testing: ptInElement() equivalent to Win32's PtInRect
What Makes WakaPAC Different?
| Developer Task | WakaPAC | Vue / React / Alpine |
|---|---|---|
| Start using in an existing page | Single script tag — no build step | Vue/React typically use build tooling; Alpine runs directly |
| Bind form inputs to state | Built-in two-way bindings via attributes | Vue/Alpine built-in; React requires explicit state handlers |
| Handle user input and system events | Central message pipeline inspired by desktop event models | Component-scoped DOM/event handlers |
| Communicate between components | Automatic parent/child messaging without manual wiring | Props/events or shared state patterns |
| Template syntax | Mustache + declarative attributes | Vue/Alpine templates; React JSX |
| Mental model | Reactive container + message-driven execution | Component tree with framework conventions |
Understanding the PAC Architecture
WakaPAC is built on the Presentation-Abstraction-Control (PAC) pattern, which cleanly separates three concerns:
1. Presentation (Your HTML)
What the user sees and interacts with - your templates and DOM elements.
2. Abstraction (Your Data)
What your application knows - your data model, computed properties, and methods.
3. Control (WakaPAC)
What keeps them in sync - WakaPAC's reactive layer that handles DOM updates, event routing, and dependency tracking.
The key difference from MVC: In PAC, the Presentation and Abstraction layers never communicate directly - all interaction goes through the Control layer. This means:
- Automatic reactivity: You never manually update the DOM - change your data and the Control layer updates the presentation
- Predictable flow: Data changes always flow through the same path, making debugging easier
- Clean separation: Your HTML templates and JavaScript data models remain completely independent
As a developer, you only work with Presentation (HTML) and Abstraction (JavaScript). The Control layer is WakaPAC itself, working behind the scenes.
When to Use WakaPAC
WakaPAC is ideal for:
- Rapid prototypes and MVPs that benefit from built-in reactivity without a build pipeline
- Content-driven sites that include interactive UI elements such as dashboards, admin panels, or forms
- Progressive enhancement of server-rendered pages with reactive behavior
- Projects that prioritize simplicity, portability, and low operational overhead
- Workflows centered on plain HTML and JavaScript with minimal tooling
- Applications that benefit from a centralized, message-driven interaction model
Consider alternatives if you need:
- Optimization strategies tailored for very large SPA architectures
- A large ecosystem of pre-built UI components and plugins
- Deep TypeScript-first tooling and framework-level type integration
- Built-in SSR or SSG pipelines tightly coupled to a framework runtime
File Size
WakaPAC is a single JavaScript file with zero dependencies. The entire framework - reactive system, component hierarchy, expression parser, browser properties - is in one file you can drop into any project.
File sizes:
- Source: ~339KB (with extensive comments)
- Minified: ~70KB
- Minified + Gzipped: ~18KB
Comparison (all sizes minified + gzipped):
- WakaPAC: ~18KB - complete framework in one file
- Alpine.js: ~15KB - minimal feature set
- Vue 3: ~33KB - runtime only — excludes optional routing/state libraries
- React + ReactDOM: ~40KB - runtime only — excludes optional routing/state libraries
Performance
WakaPAC optimizes updates through:
- Expression caching: Parsed expressions are cached to avoid repeated parsing overhead
- Value comparison: Expressions are re-evaluated, but DOM updates only happen when values actually change
- Scoped updates: Only elements in the component's binding map are processed, not the entire DOM tree
This simple approach is efficient because DOM manipulation is the expensive operation - evaluation and comparison are fast.