Fast, lightweight, and reliable distributed 64-bit ID generation for Node.js β‘ Zero dependencies β’ TypeScript-ready β’ 8.8M+ ops/sec performance
- π Ultra-fast: 8.8M+ operations per second
- π’ 64-bit time-ordered IDs: Globally unique, sortable by creation time
- π¦ Zero dependencies: Pure JavaScript implementation
- π·οΈ TypeScript-first: Full type safety and IntelliSense support
- π Universal: Works with CommonJS, ES Modules, and TypeScript
- βοΈ Lightweight: Tiny bundle size, tree-shakable
- π§ͺ Battle-tested: 100% test coverage, production-ready
- π Snowflake compatible: Drop-in replacement for Twitter Snowflake
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.
See the original presentation (slides)
Mali Akmanalp discusses Simpleflake (video)
npm install simpleflakes
const { 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)| (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
- Sortable by creation time when converted to integers
This library is optimized for speed:
// Benchmark results (operations per second)
simpleflake() // ~8.8M ops/sec
parseSimpleflake() // ~3.9M ops/sec
binary() // ~26M ops/sec
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
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 strings)
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); // 946684800000
interface SimpleFlakeStruct {
timestamp: string; // Unix timestamp as string
randomBits: string; // Random component as string
}
// 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
// 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" });
This project is written in TypeScript and includes comprehensive test coverage.
# Install dependencies
npm install
# Build TypeScript to JavaScript
npm run build
# Run tests (automatically builds first)
npm test
# Run with coverage
npm run test:coverage
# Run all CI tests (coverage)
npm run test:ci
# Type checking
npm run type-check
# Run benchmarks
npm run benchmark
# Clean build artifacts
npm run clean
- Original Presentation - Introduction to the concept
- Python Implementation - Original reference implementation
- Twitter Snowflake - Similar distributed ID system
Feature | Simpleflake | UUID v4 | UUID v7 | Nanoid | KSUID | Twitter Snowflake |
---|---|---|---|---|---|---|
Size | 64-bit | 128-bit | 128-bit | Variable | 160-bit | 64-bit |
Time-ordered | β Yes | β No | β Yes | β No | β Yes | β Yes |
Distributed | β Yes | β Yes | β Yes | β Yes | β Yes | |
Dependencies | β Zero | β crypto | β crypto | β Zero | β crypto | β System clock |
Performance | π 8.8M/sec | π ~2M/sec | π ~2M/sec | π ~5M/sec | π ~1M/sec | π ~10M/sec |
URL-friendly | β Base36 | β Long hex | β Long hex | β Custom | β Base62 | β Base36 |
Database-friendly | β Integer | β String | β String | β String | β String | β Integer |
Collision-resistant | β 8.3M/ms | β ~0 chance | β ~0 chance | β Configurable | β ~0 chance | β 4096/ms |
- 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