Skip to content
~19 KB GZIPPED WASM CORE•ACID + WAL•React, Vue & Angular Live Queries

SQLite-Grade Relational Power.
Built Native for the Browser.

Powered by a ~19 KB gzipped WebAssembly core (~51 KB raw, <100 KB gzipped bundle) with B+Tree indexes, joins, crash-proof ACID transactions, and reactive multi-tab Live Queries over OPFS & IndexedDB—with zero COOP/COEP server headers.

WASM CORE (GZIP)
~19 KB
Featherweight Binary
~51 KB raw — 95% smaller than desktop SQLite & PGlite Wasm ports
REACTIVE QUERIES
Reactive
Live Queries, UI Updates Automatically
Auto-tracked live queries across React, Vue 3, Angular & Plain JS
ASYNC VFS
OPFS + IDB
Bring Your Own VFS
OPFS + IDB + bring your own VFS with S3 HTTP Range streaming
SEARCH EXTENSION
Search
Fuzzy & BM25 Ranking
Zero-dependency typo tolerance, BM25 scoring, highlights & snippets
CUSTOM UDFS
JS UDFs
Vector Batching
Native Intl, RegExp & crypto with 128-row vectorized execution

8.7x Faster Than SQLite WASM

Multi-predicate range filtering over 5,000 rows in memory

WebDBWASM
8.7x Faster
WebDBJS
3.5x Faster
SQLite WASM
1x
Benchmark run on MacBook Pro, M1 Max 2021 (5,000 rows in memory)
View full benchmark table↓
ENGINE ARCHITECTURE

Fluent TypeScript Queries ➔ Direct VDBE Bytecode

Ported desktop databases serialize queries into SQL text strings only to re-parse them inside a multi-megabyte C parser. WebDB compiles fluent TypeScript directly into compact binary opcodes—delivering a full relational engine with a ~19 KB gzipped Wasm core (~51 KB raw).

ARCHITECTURE COMPARISON: HOW QUERIES REACH THE ENGINE 2 middle stages completely eliminated in WebDB
Legacy Wasm Ports(SQLite / PGlite Wasm)
1 MB – 5 MB+ Bundle • 4 Stages
STAGE 01
JS Query / ORM
Builds SQL text query
STAGE 02ELIMINATED
SQL String Serialization
String concat & escaping
STAGE 03 +1.5 MB BLOAT
C SQL Lexer & AST Parser
Re-tokenizes & parses text
STAGE 04
Wasm Execution
+ Asyncify stack unwinding
WebDB Direct-to-Bytecode(Browser-Native Engine)
~19 KB Gzipped Wasm (~51 KB Raw) • 2 Stages
STAGE 01
Fluent Query Builder
db.from("orders").where(...)
⚡ DIRECT COMPILER — SKIPS SQL STRING & C PARSER ENTIRELY
Emits binary opcodes directly from TypeScript AST in < 0.05ms
STAGE 02
VDBE Binary Bytecode
OP_INDEX_SCAN ➔ OP_RESULT_ROW
1// 1. Define relational schema with typed binary columns & B+Tree indexes
2await db.createTable("orders", [
3 { name: "id", type: "UUID", primaryKey: true }, // Packed into 16 raw bytes
4 { name: "customer_id", type: "UUID", notNull: true },
5 { name: "status", type: "TEXT", notNull: true },
6 { name: "total", type: "FLOAT64", notNull: true },
7]);
8await db.createIndex("orders", ["status", "total"]);
9 
10// 2. Fluent relational query with JOINs, aggregates & subqueries
11const topCustomers = await db
12 .from("orders")
13 .join("customers", "orders.customer_id", "=", "customers.id")
14 .select("customers.name as customer")
15 .sum("orders.total", "lifetimeValue")
16 .count("*", "orderCount")
17 .where("orders.status", "=", "paid")
18 .groupBy("customers.name")
19 .having("lifetimeValue", ">=", 500)
20 .orderBy("lifetimeValue", "desc")
21 .limit(10)
22 .toArray();
ZERO-BLOAT FRAMEWORK INTEGRATIONS

Reactive Live Queries for React, Vue, Angular & JS

Stop wiring manual event buses around database writes. WebDB automatically tracks every table accessed by your query—including JOINs and nested subqueries—and pushes instant updates across components and open browser tabs.

OrderDashboard.tsxHooks + Context
1import { useLiveQuery } from "@ahmadmoussawi/webdb/react";
2 
3export function OrderDashboard({ status }: { status: string }) {
4 // Automatically tracks 'orders' AND joined 'customers' table!
5 const { data: orders, isLoading } = useLiveQuery(
6 () =>
7 db
8 .from("orders")
9 .join("customers", "orders.customer_id", "=", "customers.id")
10 .where("orders.status", "=", status)
11 .orderBy("orders.id", "desc"),
12 [status],
13 { initialData: [] }
14 );
15 
16 // Mutating 'orders' (here or in another tab) automatically updates 'orders' & re-renders!
17 const markPaid = (id: string) =>
18 db.from("orders").where("id", "=", id).update({ status: "paid" });
19 
20 if (isLoading) return <p>Loading live orders...</p>;
21 return <OrderTable rows={orders} onMarkPaid={markPaid} />;
22}
AUTO-TRACKED AST

Smart Table & JOIN Tracking

Inspects base tables, JOINs, and subqueries automatically—only re-executing when relevant tables mutate.

MULTI-TAB SYNC

Cross-Tab & Worker Reactivity

Commits in another browser tab or Web Worker broadcast via BroadcastChannel and update your UI in real time.

< 1 KB SUBPATHS

Zero Core Bundle Bloat

Each framework adapter is an isolated subpath export under 1 KB gzipped with optional peer dependencies.

ZERO-DEPENDENCY SEARCH EXTENSION

Instant Typo-Tolerant & BM25 Full-Text Search

No bloated C engines or heavy shadow tables. Powered entirely by web platform primitives (Intl.Segmenter, Unicode normalization, bounded Damerau-Levenshtein) and vectorized BM25 ranking right inside WebDB's query engine.

SearchComponent.tsx @ahmadmoussawi/webdb/search
1import { WebDB } from "@ahmadmoussawi/webdb";
2import { createSearchIndex } from "@ahmadmoussawi/webdb/search";
3import { useLiveQuery } from "@ahmadmoussawi/webdb/react"; // or vue, angular
4 
5const db = await WebDB.open({ name: "docs_app", storage: "idb" });
6 
7// 1. Create a zero-overhead search index over your table (0 shadow tables!)
8const articleSearch = createSearchIndex(db, {
9 table: "articles",
10 fields: [
11 { name: "title", weight: 10, highlight: true }, // exact > prefix > typos
12 { name: "tags", weight: 5 },
13 { name: "body", weight: 1, snippet: true }, // extracts contextual <mark> snippets
14 ],
15});
16 
17// 2. Reactively search with typo tolerance and live query updates as you type
18const { data: results } = useLiveQuery(
19 () =>
20 articleSearch.buildQuery(searchQuery.value, {
21 fuzzy: true, // Damerau-Levenshtein typo tolerance ("reactve" -> "reactive")
22 prefix: true, // Auto-completes word prefixes ("persist" -> "persistence")
23 filter: (qb) => (category.value ? qb.where("category", "=", category.value) : qb),
24 limit: 20,
25 }),
26 [searchQuery, category],
27 { db }
28);
TABLE-NATIVE

Zero Storage Overhead

Unlike SQLite FTS5 which creates 3–5 shadow tables, SearchIndex queries your existing tables directly. Always 100% in sync with zero write penalties.

PROBABILISTIC RANKING

Fuzzy & BM25 Ranking

Weighted multi-field scoring, prefix matching, and Robertson-Spärck Jones BM25 term frequency / document length normalization with zero external dependencies.

UI-READY

Hit Highlights & Snippets

Automatically wraps matched keywords in safe <mark> tags and extracts contextual snippets (~140 chars) around matches for instant rendering.

ZERO-BLOAT EXTENSIBILITY

Native JavaScript UDFs & Vector Batching

Instead of compiling megabytes of ICU, PCRE, and date libraries into WebAssembly, run JavaScript functions directly inside your queries. Leverage native browser APIs (Intl, RegExp, crypto) with 128-row vectorized execution.

custom-functions.ts db.registerBatchedFunction()
1import { WebDB, DataType } from "@ahmadmoussawi/webdb";
2 
3const db = await WebDB.open({ name: "app_db", storage: "idb" });
4 
5// 1. Leverage native Browser Web APIs as SQL functions (zero binary bloat!)
6db.registerFunction("format_money", (amount, currency = "USD") => {
7 if (amount === null) return null;
8 return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(Number(amount));
9});
10 
11// 2. Vectorized batching: processes 128 rows at once to eliminate call overhead
12db.registerBatchedFunction(
13 "calc_discount",
14 (argsBatch) => {
15 // Called once per 128-row slice instead of 128 individual JS call frames:
16 return argsBatch.map(([price, discountPercent]) =>
17 Number(price) * (1 - Number(discountPercent) / 100)
18 );
19 },
20 { returnType: DataType.FLOAT64, deterministic: true }
21);
22 
23// 3. Use seamlessly inside fluent queries: .select(), .where(), .orderBy()
24const deals = await db
25 .from("products")
26 .select([
27 "id",
28 "name",
29 "format_money(calc_discount(price, 20)) as discounted_price",
30 ])
31 .where("calc_discount(price, 20) < 50")
32 .toArray();
BROWSER-NATIVE

Web APIs in Queries

Call Intl.DateTimeFormat, Intl.NumberFormat, and RegExp directly in SQL-like expressions without bloating the core Wasm binary.

HIGH-THROUGHPUT

Vectorized 128-Row Batching

registerBatchedFunction dispatches rows in power-of-two chunks (128 rows), eliminating per-row JS call frame overhead and aligning with CPU cache lines.

QUERY OPTIMIZER

Typed Returns & Determinism

Declare return types (INT32, FLOAT64, TEXT) and deterministic flags so the query compiler optimizes VDBE registers and bytecode dispatch.

IN-MEMORY BENCHMARKS • MACBOOK PRO, APPLE M1 MAX 2021 (5,000 ROWS)

Up to 8.7x Faster Than Official SQLite WASM

Because WebDB compiles TypeScript directly into VDBE binary opcodes—skipping SQL string serialization and a multi-megabyte C parser—our ~19 KB gzipped Wasm core (~51 KB raw) outperforms official SQLite WASM (:memory:) across all 6 relational workloads, while even WebDB Pure JS beats SQLite WASM in 4 out of 6 benchmarks.

Workload Scenario (5,000 Rows • In-Memory)
WebDB (C / WASM)
WebDB (Pure JS)
SQLite WASM (:memory:)
WebDB vs SQLite
1. Range Filter Scan
Multi-predicate range filtering over 5,000 rows in memory
1,091.7 ops/s
Mean 0.92 ms • P95 1.00 ms
444.0 ops/s
Mean 2.25 ms • P95 2.32 ms
125.9 ops/s
Mean 7.94 ms • P95 10.46 ms
8.7x faster
2. Bulk Batch Insert
5,000 rows inserted in a single ACID transaction
359,152 ops/s
Mean 13.92 ms • P95 14.41 ms
301,932 ops/s
Mean 16.56 ms • P95 17.14 ms
156,576 ops/s
Mean 31.93 ms • P95 33.57 ms
2.3x faster
3. Batch Point Lookups (IN)
Primary-key B+Tree point lookups via WHERE id IN (...)
334,448 ops/s
Mean 0.30 ms • P95 0.34 ms
234,192 ops/s
Mean 0.43 ms • P95 0.44 ms
148,148 ops/s
Mean 0.68 ms • P95 0.69 ms
2.3x faster
4. Indexed 2-Table JOIN
Relational INNER JOIN across 2 indexed tables
755.3 ops/s
Mean 1.32 ms • P95 1.42 ms
494.6 ops/s
Mean 2.02 ms • P95 2.07 ms
440.1 ops/s
Mean 2.27 ms • P95 2.36 ms
1.7x faster
5. Filter + Sort + Limit
Compound WHERE filter + ORDER BY + LIMIT pagination
2,381.0 ops/s
Mean 0.42 ms • P95 0.47 ms
681.7 ops/s
Mean 1.47 ms • P95 1.52 ms
1,483.7 ops/s
Mean 0.67 ms • P95 0.74 ms
1.6x faster
6. Analytics Aggregation
GROUP BY with COUNT, SUM & AVG aggregate functions
2,392.3 ops/s
Mean 0.42 ms • P95 0.56 ms
559.0 ops/s
Mean 1.79 ms • P95 1.81 ms
1,689.2 ops/s
Mean 0.59 ms • P95 0.60 ms
1.4x faster
WEB-FIRST CAPABILITIES

Everything You Need for Local-First Web Apps

Engineered from the ground up around browser storage primitives, asynchronous I/O, and compact 4 KB slotted pages.

ASYNC VIRTUAL FILE SYSTEM

OPFS, IndexedDB, In-Memory & S3 HTTP Range Streaming

Unlike desktop C ports that rely on Emscripten’s slow Asyncify stack rewriting, WebDB suspends cleanly on 4 KB page faults. Persist data in high-speed OPFS or IndexedDB, or query multi-gigabyte read-only databases hosted on S3/CDN via Range: bytes=X-Y requests without downloading the file.

OPFS SyncAccessHandleIndexedDB FallbackHttpVfsAdapter (4KB Range)Custom IVfsAdapter
ACID & OFFLINE SYNC

Crash-Proof WAL, Web Locks & Coalesced Outbox Sync

Write-Ahead Logging with IEEE 802.3 CRC32 checksums on every 4 KB page guarantees zero corruption across sudden tab closes. Multi-tab readers and writers coordinate automatically via navigator.locks, while onBeforeCommit and changeset.coalesced() make bidirectional offline sync effortless.

CRC32 Page ChecksumsNested SavepointsWeb Locks Coordinationchangeset.coalesced()
COMPACT STORAGE

16-Byte Binary UUIDv7 & ULID

Packs UUIDs and ULIDs into 16 raw bytes (58% smaller than 36-char text strings) for sequential, zero-fragmentation B+Tree index inserts.

COST-BASED PLANNER

Composite B+Trees & .explain()

Up to 8-column composite indexes, index-assisted sorting, hash/nested joins, and full VDBE bytecode disassembly via .explain().

SEARCH & EXTENSIBILITY

Fuzzy Search & Browser UDFs

Official @ahmadmoussawi/webdb/search extension with typo-tolerant matching, BM25 ranking, and highlights—plus custom JS functions using native Intl and RegExp.

INSTANT COLD START

Compressed Snapshots (.webdb.gz)

Export and restore full indexed databases as Uint8Array or compressed .webdb.gz images in milliseconds—or share live databases via Studio URLs.

ARCHITECTURAL COMPARISON

How WebDB Compares

Purpose-built to give you the relational power of SQLite with the tiny footprint and zero-config ergonomics of native browser APIs.

CapabilityRaw IndexedDB / WrappersPorted Desktop Wasm (SQLite / PGlite)
WebDBBrowser-Native
Bundle Footprint~15–40 KB (No SQL engine)1 MB – 5 MB+ Wasm binary~19 KB Gzipped Wasm Core (~51 KB raw, <100 KB bundle)
Relational Joins & SubqueriesManual JS loops & cursor mergesFull SQL text parserDirect-to-Bytecode JOINs, GROUP BY & Subqueries
Reactive UI HooksBasic table-level polling/hooksDIY wrappers / manual invalidation<1 KB Subpaths for React, Vue, Angular & JS with auto-JOIN tracking
Fuzzy & Full-Text SearchManual regex / external depsFTS5 (heavy shadow tables)Zero-Dependency Search — Fuzzy, BM25 & highlighting with 0 shadow tables
Server Isolation HeadersNone required Requires COOP/COEP for SharedArrayBuffer Zero Headers — runs on any static host or CDN
Async Storage & S3 StreamingIndexedDB onlyEmscripten Asyncify shimsNative Async VFS (OPFS, IDB, Memory, HTTP Range)
IN-BROWSER DATABASE IDESupports .webdb.gz & Remote URLs

WebDB Studio Playground

Write and execute queries in Monaco Editor, inspect binary 4 KB slotted pages in real time, visualize B+Tree structures, and open remote .webdb.gz databases directly via shareable URLs.

LIVE PERFORMANCE SUITERuns on Your Hardware

In-Browser Benchmark Suite

Measure real-time ops/sec throughput, latency percentiles, and memory footprint against Raw JS Arrays, Native IndexedDB, and official SQLite WASM directly in your browser.

Released under the MIT License.