Fast, lightweight, and reliable distributed 64-bit ID generation for Node.js and the web Zero dependencies β’ TypeScript-ready β’ 10M+ ops/sec performance
- β‘ 10M+ ops/sec - Ultra-fast performance
- π’ Time-oriented 64-bit IDs - Globally unique, sortable by creation time
- 0οΈβ£ Zero dependencies - Pure JavaScript, lightweight bundle
- π·οΈ TypeScript-ready - Full type safety and universal module support
- π Production-ready - 100% test coverage, Snowflake compatible
- What is Simpleflake?
- Installation
- Quick Start
- Advanced Usage
- ID Structure
- Performance
- Architecture
- Use Cases
- API Reference
- Migration Guide
- Comparison
- Contributing
Simpleflake generates unique 64-bit integers that are:
- Time-ordered - IDs generated later are numerically larger
- Distributed-safe - No coordination needed between multiple generators
- Compact - Fits in a 64-bit integer (vs UUID's 128 bits)
- URL-friendly - Can be represented as short strings
Perfect for database primary keys, distributed system IDs, and anywhere you need fast, unique identifiers.
Works in Node.js and modern browsers with BigInt and Web Crypto support.
- Original Presentation - Introduction to the concept by Mali Akmanalp
- Video Discussion - Detailed explanation and use cases
- Python Implementation - Original reference implementation
- Twitter Snowflake - Similar distributed ID system
npm install simpleflakesconst { simpleflake } = require('simpleflakes');
// Generate a unique ID
const id = simpleflake();
console.log(id); // 4234673179811182512n (BigInt)
// Convert to different formats
console.log(id.toString()); // "4234673179811182512"
console.log(id.toString(16)); // "3ac494d21e84f7b0" (hex)
console.log(id.toString(36)); // "w68acyhy50hc" (base36 - shortest)import { simpleflake, parseSimpleflake, type SimpleflakeStruct } from 'simpleflakes';
// Generate with full type safety
const id: bigint = simpleflake();
// Parse the ID to extract timestamp and random bits
const parsed: SimpleflakeStruct = parseSimpleflake(id);
console.log(parsed.timestamp); // "1693244847123" (Unix timestamp as string)
console.log(parsed.randomBits); // "4567234" (Random component as string)// Generate with custom timestamp and random bits
const customId = simpleflake(
Date.now(), // timestamp (default: Date.now())
12345, // random bits (default: 23-bit random)
Date.UTC(2000, 0, 1) // epoch (default: Year 2000)
);import { binary, extractBits } from 'simpleflakes';
const id = simpleflake();
// View binary representation
console.log(binary(id));
// Output: "0011101011000100100100110100001000011110100001001111011110110000"
// Extract specific bit ranges
const timestampBits = extractBits(id, 23n, 41n); // Extract 41 bits starting at position 23
const randomBits = extractBits(id, 0n, 23n); // Extract first 23 bits// Generate multiple IDs efficiently
function generateBatch(count) {
const ids = [];
for (let i = 0; i < count; i++) {
ids.push(simpleflake());
}
return ids;
}
const batch = generateBatch(1000);
console.log(`Generated ${batch.length} unique IDs`);Each 64-bit simpleflake ID contains:
| <------- 41 bits -------> | <- 23 bits -> |
|---|---|
| Timestamp | Random |
| (milliseconds from epoch) | (0-8388607) |
- 41 bits timestamp: Milliseconds since epoch (Year 2000)
- 23 bits random: Random number for uniqueness within the same millisecond
- Total: 64 bits = fits in a signed 64-bit integer
This gives you:
- 69+ years of timestamp range (until year 2069)
- 8.3 million unique IDs per millisecond
- Extremely low collision chance - 1 in 8.3 million per millisecond
- Sortable by creation time when converted to integers
This library is optimized for speed:
| Operation | Ops/sec | Time/op |
|---|---|---|
simpleflake() |
11,468,350 | 87.20 ns |
simpleflake(timestamp, randomBits, epoch) |
14,557,764 | 68.69 ns |
binary() |
22,794,523 | 43.87 ns |
BigInt() |
15,933,159 | 62.76 ns |
parseSimpleflake() |
6,722,882 | 148.75 ns |
Benchmark command:
npm run benchmarkBenchmark environment:
- Node.js v24.14.1
- Linux 6.6.87.2-microsoft-standard-WSL2 (WSL2)
- AMD Ryzen 7 5800X3D 8-Core Processor
- 8 cores / 16 threads
Results will vary across CPUs, Node.js versions, power profiles, and native Linux vs. WSL2 environments.
Perfect for high-throughput applications requiring millions of IDs per second.
- Database-friendly: Most databases optimize for 64-bit integers
- Memory efficient: Half the size of UUIDs (128-bit)
- Performance: Integer operations are faster than string operations
- Sortable: Natural ordering by creation time
- Compact URLs: Shorter than UUIDs when base36-encoded
No coordination required between multiple ID generators:
- Clock skew tolerant: Small time differences between servers are fine
- Random collision protection: 23 random bits provide 8.3M combinations per millisecond
- High availability: Each service can generate IDs independently
- Cryptographic randomness by default: simpleflake() uses Web Crypto in browsers and Node's built-in crypto implementation in Node.js.
- No Math.random() fallback: this keeps the default entropy source strong and avoids silently weakening collision resistance.
- Runtime requirements: modern browsers need
crypto.getRandomValues()and Node.js needs version 16 or newer. - Deterministic or legacy environments: pass
randomBitsexplicitly if you need repeatable output or if you are targeting a runtime without crypto support.
// Perfect for database IDs - time-ordered and unique
const userId = simpleflake();
await db.users.create({ id: userId.toString(), name: "John" });// Each service can generate IDs independently
const serviceAId = simpleflake(); // Service A
const serviceBId = simpleflake(); // Service B
// No coordination needed, guaranteed unique across services// Generate compact URL identifiers
const shortId = simpleflake().toString(36); // "w68acyhy50hc"
const url = `https://short.ly/${shortId}`;// Time-ordered event IDs for chronological processing
const eventId = simpleflake();
await analytics.track({ eventId, userId, action: "click" });Generates a unique 64-bit ID.
Parameters:
timestamp(number, optional): Unix timestamp in milliseconds. Default:Date.now()randomBits(number, optional): Random bits (0-8388607). Default: random 23-bit numberepoch(number, optional): Epoch start time. Default:Date.UTC(2000, 0, 1)
Returns: BigInt - The generated ID
const id = simpleflake();
const customId = simpleflake(Date.now(), 12345, Date.UTC(2000, 0, 1));Parses a simpleflake ID into its components.
Parameters:
flake(bigint | string | number): The ID to parse
Returns: Object with timestamp and randomBits properties (both bigint)
const parsed = parseSimpleflake(4234673179811182512n);
console.log(parsed.timestamp); // "1693244847123"
console.log(parsed.randomBits); // "4567234"Converts a number to binary string representation.
Parameters:
value(bigint | string | number): Value to convertpadding(boolean, optional): Whether to pad to 64 bits. Default:true
Returns: String - Binary representation
console.log(binary(42n)); // "0000000000000000000000000000000000000000000000000000000000101010"
console.log(binary(42n, false)); // "101010"Extracts a portion of bits from a number.
Parameters:
data(bigint | string | number): Source datashift(bigint): Starting bit position (0-based from right)length(bigint): Number of bits to extract
Returns: BigInt - Extracted bits as number
const bits = extractBits(0b11110000n, 4n, 4n); // Extract 4 bits starting at position 4
console.log(bits); // 15n (0b1111)The epoch start time (January 1, 2000 UTC) as Unix timestamp.
import { SIMPLEFLAKE_EPOCH } from 'simpleflakes';
console.log(SIMPLEFLAKE_EPOCH); // 946684800000interface SimpleflakeStruct {
timestamp: bigint; // Unix timestamp as bigint (since 2000)
randomBits: bigint; // Random component as bigint
}// Before (UUID v4)
import { v4 as uuidv4 } from 'uuid';
const id = uuidv4(); // "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// After (Simpleflake)
import { simpleflake } from 'simpleflakes';
const id = simpleflake().toString(36); // "w68acyhy50hc" (shorter!)// Simpleflake is backwards compatible with Snowflake structure
// Just different bit allocation:
// - Snowflake: 41 bits timestamp + 10 bits machine + 12 bits sequence
// - Simpleflake: 41 bits timestamp + 23 bits random
//
// *double-check epoch| Library | Size | Time-ordered | Performance |
|---|---|---|---|
| Simpleflake | 64-bit | β Yes | β‘ 8.8M/sec |
| UUID v4 | 128-bit | β No | πΈ ~2M/sec |
| UUID v7 | 128-bit | β Yes | πΈ ~2M/sec |
| Nanoid | Variable | β No | β‘ ~5M/sec |
| KSUID | 160-bit | β Yes | πΈ ~1M/sec |
| Twitter Snowflake | 64-bit | β Yes | β‘ ~10M/sec |
| Library | Dependencies | Database-friendly | URL-friendly | Distributed |
|---|---|---|---|---|
| Simpleflake | β Zero | β Integer | β Base36 | β Yes |
| UUID v4 | β crypto | β String | β Long hex | β Yes |
| UUID v7 | β crypto | β String | β Long hex | β Yes |
| Nanoid | β Zero | β String | β Custom | β Yes |
| KSUID | β crypto | β String | β Base62 | β Yes |
| Twitter Snowflake | β System clock | β Integer | β Base36 |
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Original concept by Mali Akmanalp
- TypeScript port and optimizations by Leo Dutra
- Inspired by Twitter Snowflake