High-performance embedded vector database for Browser, Node, and Edge
npm install edgevecbash
npm install edgevec
`
`typescript
import init, { EdgeVec } from 'edgevec';
await init();
// Create index (768D for embeddings like OpenAI, Cohere)
const db = new EdgeVec({ dimensions: 768 });
// Insert vectors with metadata (v0.6.0)
const vector = new Float32Array(768).map(() => Math.random());
const id = db.insertWithMetadata(vector, {
category: "books",
price: 29.99,
inStock: true
});
// Search with filter expression (v0.6.0)
const query = new Float32Array(768).map(() => Math.random());
const results = db.searchWithFilter(query, 'category = "books" AND price < 50', 10);
// Fast BQ search with rescoring — 32x less memory, 95% recall (v0.6.0)
const fastResults = db.searchBQ(query, 10);
// Monitor memory pressure (v0.6.0)
const pressure = db.getMemoryPressure();
if (pressure.level === 'warning') {
db.compact(); // Free deleted vectors
}
`
---
Interactive Demos
Try EdgeVec directly in your browser:
| Demo | Description |
|:-----|:------------|
| Filter Playground v0.7.0 | Visual filter builder with live sandbox (NEW!) |
| v0.6.0 Cyberpunk Demo | BQ vs F32 comparison, metadata filtering, memory pressure |
| Demo Hub | All demos in one place |
Run locally:
| Demo | Path |
|:-----|:-----|
| SIMD Benchmark | wasm/examples/simd_benchmark.html |
| Benchmark Dashboard | wasm/examples/benchmark-dashboard.html |
| Soft Delete Demo | wasm/examples/soft_delete.html |
| Main Demo | wasm/examples/index.html |
`bash
Run demos locally
git clone https://github.com/matte1782/edgevec.git
cd edgevec
python -m http.server 8080
Open http://localhost:8080/wasm/examples/index.html
`
---
Performance
EdgeVec v0.7.0 uses SIMD instructions for 2x+ faster vector operations on modern browsers.
$3
| Dimension | Dot Product | L2 Distance | Throughput |
|:----------|:------------|:------------|:-----------|
| 128 | 55 ns | 66 ns | 2.3 Gelem/s |
| 384 | 188 ns | 184 ns | 2.1 Gelem/s |
| 768 | 374 ns | 358 ns | 2.1 Gelem/s |
| 1536 | 761 ns | 693 ns | 2.1 Gelem/s |
$3
| Scale | EdgeVec | Target | Status |
|:------|:--------|:-------|:-------|
| 1k vectors | 380 us | <1 ms | 2.6x under |
| 10k vectors | 938 us | <1 ms | PASS |
$3
| Operation | Time | Throughput |
|:----------|:-----|:-----------|
| 768-bit pair | 4.5 ns | 40 GiB/s |
| Batch 10k | 79 us | 127 Melem/s |
$3
| Browser | SIMD | Performance |
|:--------|:-----|:------------|
| Chrome 91+ | YES | Full speed |
| Firefox 89+ | YES | Full speed |
| Safari 16.4+ | YES | Full speed (macOS) |
| Edge 91+ | YES | Full speed |
| iOS Safari | NO | Scalar fallback |
> Note: iOS Safari doesn't support WASM SIMD. EdgeVec automatically uses scalar
> fallback, which is ~2x slower but still functional.
$3
| Package | Size (gzip) | Notes |
|:--------|:------------|:------|
| edgevec | 217 KB | SIMD enabled (541 KB uncompressed) |
Full benchmark report ->
---
Database Features
$3
32x memory reduction with minimal recall loss:
`javascript
// BQ is auto-enabled for dimensions divisible by 8
const db = new EdgeVec({ dimensions: 768 });
// Raw BQ search (~85% recall, ~5x faster)
const bqResults = db.searchBQ(query, 10);
// BQ + rescore (~95% recall, ~3x faster)
const rescoredResults = db.searchBQRescored(query, 10, 5);
`
| Mode | Memory (100k × 768D) | Speed | Recall@10 |
|:-----|:---------------------|:------|:----------|
| F32 (baseline) | ~300 MB | 1x | 100% |
| BQ raw | ~10 MB | 5x | ~85% |
| BQ + rescore(5) | ~10 MB | 3x | ~95% |
$3
Insert vectors with metadata, search with SQL-like filter expressions:
`javascript
// Insert with metadata
db.insertWithMetadata(vector, {
category: "electronics",
price: 299.99,
tags: ["featured", "sale"]
});
// Search with filter
db.searchWithFilter(query, 'category = "electronics" AND price < 500', 10);
db.searchWithFilter(query, 'tags ANY ["featured"]', 10); // Array membership
// Complex expressions
db.searchWithFilter(query,
'(category = "electronics" OR category = "books") AND price < 100',
10
);
`
Operators: =, !=, >, <, >=, <=, AND, OR, NOT, ANY
Filter syntax documentation ->
$3
For functional composition style, use standalone filter functions:
`typescript
import { filter, and, or, eq, gt, lt, between, contains } from 'edgevec';
// Simple conditions
const byCategory = eq('category', 'electronics');
const expensive = gt('price', 1000);
// Compose with and/or
const query = filter(
and(
eq('category', 'electronics'),
gt('price', 100),
lt('price', 1000)
)
);
// Nested logic
const complex = filter(
and(
eq('status', 'active'),
or(
eq('brand', 'Apple'),
eq('brand', 'Samsung'),
eq('brand', 'Google')
)
)
);
// Use in search
const results = await index.search(embedding, 10, { filter: query });
`
Available Functions:
- Comparison: eq, ne, gt, lt, ge, le, between
- String: contains, startsWith, endsWith, like
- Array: inArray, notInArray, any, all, none
- Null: isNull, isNotNull
- Logic: and, or, not, filter
- Special: matchAll, matchNone
25 copy-paste ready filter examples ->
$3
Monitor and control WASM heap usage:
`javascript
const pressure = db.getMemoryPressure();
// { level: 'normal', usedBytes: 52428800, totalBytes: 268435456, usagePercent: 19.5 }
if (pressure.level === 'warning') {
db.compact(); // Free deleted vectors
}
if (!db.canInsert()) {
console.warn('Memory critical, inserts blocked');
}
`
$3
`javascript
// O(1) soft delete
db.softDelete(id);
// Check status
console.log('Live:', db.liveCount());
console.log('Deleted:', db.deletedCount());
// Reclaim space when needed
if (db.needsCompaction()) {
const result = db.compact();
console.log(Removed ${result.tombstones_removed} tombstones);
}
`
$3
`javascript
// Save to IndexedDB (browser) or filesystem
await db.save("my-vector-db");
// Load existing database
const db = await EdgeVec.load("my-vector-db");
`
$3
`javascript
const config = new EdgeVecConfig(768);
config.quantized = true; // Enable SQ8 quantization
// 3.6x memory reduction: 3.03 GB -> 832 MB at 1M vectors
`
---
Rust Usage
`rust
use edgevec::{HnswConfig, HnswIndex, VectorStorage};
fn main() -> Result<(), Box> {
let config = HnswConfig::new(768);
let mut storage = VectorStorage::new(&config, None);
let mut index = HnswIndex::new(config, &storage)?;
// Insert
let vector = vec![0.1; 768];
let id = index.insert(&vector, &mut storage)?;
// Search
let query = vec![0.1; 768];
let results = index.search(&query, 10, &storage)?;
// Soft delete
index.soft_delete(id)?;
Ok(())
}
`
---
React Integration (v0.8.0)
EdgeVec provides React hooks for seamless integration with React 18+ applications.
$3
`bash
npm install edgevec react
`
$3
Initialize an EdgeVec database with automatic WASM loading:
`tsx
import { useEdgeVec } from 'edgevec/react';
function App() {
const { db, isReady, isLoading, error, stats, save } = useEdgeVec({
dimensions: 384,
persistName: 'my-vectors', // Optional: enables IndexedDB persistence
});
if (isLoading) return Loading EdgeVec...;
if (error) return Error: {error.message};
if (!isReady) return null;
return {stats?.count} vectors indexed;
}
`
$3
Perform reactive searches that automatically update when the query changes:
`tsx
import { useState } from 'react';
import { useEdgeVec, useSearch } from 'edgevec/react';
import { eq, and, gt } from 'edgevec';
function SearchComponent() {
const { db, isReady } = useEdgeVec({ dimensions: 384 });
const [queryVector, setQueryVector] = useState(null);
const { results, isSearching, searchTime } = useSearch(db, {
vector: queryVector,
k: 10,
filter: and(eq('category', 'documents'), gt('score', 0.5)),
enabled: isReady && queryVector !== null,
debounceMs: 300, // Debounce rapid changes
});
return (
{isSearching && Searching...}
{searchTime && Found in {searchTime.toFixed(1)}ms}
{results.map(result => (
-
Score: {result.score.toFixed(4)}
))}
);
}
`
$3
`tsx
import { useState, useCallback } from 'react';
import { useEdgeVec, useSearch } from 'edgevec/react';
import { and, eq, gt } from 'edgevec';
export function SemanticSearch() {
const { db, isReady, stats, save } = useEdgeVec({
dimensions: 384,
persistName: 'semantic-search-demo'
});
const [query, setQuery] = useState('');
const [queryVector, setQueryVector] = useState(null);
const { results, isSearching, searchTime } = useSearch(db, {
vector: queryVector,
k: 10,
filter: and(eq('type', 'document'), gt('relevance', 0.5)),
enabled: isReady,
debounceMs: 300
});
const handleSearch = useCallback(async () => {
if (query.trim()) {
// Your embedding function here
const embedding = await getEmbedding(query);
setQueryVector(embedding);
}
}, [query]);
if (!isReady) return Initializing...;
return (
setQuery(e.target.value)} />
{stats?.count} vectors | {isSearching ? 'Searching...' : ${searchTime?.toFixed(1)}ms}
{results.map(r => (
- ID: {r.id}, Score: {r.score.toFixed(4)}
))}
);
}
`
$3
#### useEdgeVec(options)
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
| dimensions | number | required | Vector dimensions |
| persistName | string | undefined | IndexedDB store name |
| efConstruction | number | 200 | HNSW build parameter |
| m | number | 16 | HNSW connections |
Returns: { db, isReady, isLoading, error, stats, reload, save }
#### useSearch(db, options)
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
| vector | Float32Array \| number[] \| null | required | Query vector |
| k | number | 10 | Number of results |
| filter | FilterExpression \| string | undefined | Filter expression |
| enabled | boolean | true | Enable/disable search |
| debounceMs | number | 0 | Debounce delay |
| includeMetadata | boolean | false | Include metadata |
| includeVectors | boolean | false | Include vectors |
Returns: { results, isSearching, error, searchTime, refetch }
---
Vue Integration (v0.8.0)
EdgeVec provides Vue 3 composables for seamless integration with Vue 3.3+ applications.
$3
`bash
npm install edgevec vue
`
$3
Initialize an EdgeVec database with automatic WASM loading:
`vue
Loading EdgeVec...
Error: {{ error.message }}
{{ stats?.count }} vectors indexed
`
$3
Perform reactive searches that automatically update when the query changes:
`vue
Searching...
Found in {{ searchTime.toFixed(1) }}ms
-
Score: {{ result.score.toFixed(4) }}
`
$3
`vue
Initializing...
{{ stats?.count }} vectors | {{ isSearching ? 'Searching...' : ${searchTime?.toFixed(1)}ms }}
-
ID: {{ r.id }}, Score: {{ r.score.toFixed(4) }}
`
$3
#### useEdgeVec(options)
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
| dimensions | number | required | Vector dimensions |
| persistName | string | undefined | IndexedDB store name |
| efConstruction | number | 200 | HNSW build parameter |
| m | number | 16 | HNSW connections |
Returns: { db, isReady, isLoading, error, stats, reload, save }
All reactive values are Vue refs (use .value to access).
#### useSearch(db, options)
| Option | Type | Default | Description |
|:-------|:-----|:--------|:------------|
| vector | Ref\ | required | Query vector (can be ref) |
| k | number \| Ref\ | 10 | Number of results |
| filter | FilterExpression \| string \| Ref | undefined | Filter expression |
| enabled | boolean \| Ref\ \| ComputedRef | true | Enable/disable search |
| debounceMs | number | 0 | Debounce delay |
| includeMetadata | boolean | false | Include metadata |
| includeVectors | boolean | false | Include vectors |
Returns: { results, isSearching, error, searchTime, refetch }
$3
| Feature | React | Vue |
|:--------|:------|:----|
| State | useState returns value | useEdgeVec returns refs (.value) |
| Enabled condition | enabled: isReady && vector !== null | enabled: computed(() => isReady.value && vector.value !== null)` |