CI npm coveralls status npm downloads Bundle Size TypeScript Node.js Performance Last Commit FOSSA Status
Fast, lightweight, and reliable distributed 64-bit ID generation for Node.js Zero dependencies β’ TypeScript-ready β’ 8.8M+ ops/sec performance
- β‘ 8.8M+ 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.
- 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 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 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:
// 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
// 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); // 946684800000
interface 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