npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

keyboard-history

v1.1.4

Published

A browser-based npm package that captures and stores keyboard interaction data including key presses, duration, and timestamps

Readme

KeyboardHistory

A lightweight, browser-based npm package that captures and stores detailed keyboard interaction data including key presses, duration, and precise timestamps. Perfect for analytics, debugging, user experience research, and input behavior analysis.

Features

  • 🎯 Real-time keyboard event capture - Records key presses with millisecond precision
  • ⏱️ Duration tracking - Measures how long each key is held down
  • 📊 Detailed event data - Captures key identifiers, physical key codes, and timestamps
  • 🔧 Configurable - Customizable event limits, repeat handling, and precision settings
  • 🎬 Event replay - Simulate recorded keyboard events with original timing
  • 🚀 Lightweight - Zero dependencies, minimal footprint
  • 🌐 Browser-focused - Designed specifically for web applications
  • 📦 TypeScript support - Full type definitions included

Installation

npm install keyboard-history

Quick Start

import { KeyboardHistory } from 'keyboard-history';

// Create instance with default settings
const keyboardHistory = new KeyboardHistory();

// Start recording keyboard events
keyboardHistory.start();

// ... user types on keyboard ...

// Stop recording
keyboardHistory.stop();

// Get all recorded events
const events = keyboardHistory.getRecordedKeys();
console.log(events);
// Output: [
//   { key: 'h', duration: 120, timestamp: 1634567890123, code: 'KeyH' },
//   { key: 'e', duration: 95, timestamp: 1634567890245, code: 'KeyE' },
//   { key: 'l', duration: 110, timestamp: 1634567890356, code: 'KeyL' },
//   ...
// ]

// Replay the recorded events with original timing
keyboardHistory.replay();

Configuration

import { KeyboardHistory } from 'keyboard-history';

const keyboardHistory = new KeyboardHistory({
  maxEvents: 5000,          // Maximum events to store (default: 10000)
  captureRepeats: false,    // Capture key repeat events (default: true)
  timestampPrecision: 2,    // Decimal places for timestamps (default: 3)
  replayEventName: 'myCustomReplayEvent' // Custom event name for replay (default: 'keyboardHistoryReplay')
});

API Documentation

Constructor

new KeyboardHistory(config?: KeyboardHistoryConfig)

Creates a new KeyboardHistory instance.

Parameters:

  • config (optional): Configuration object

Configuration Options:

  • maxEvents?: number - Maximum number of events to store (default: 10000)
  • captureRepeats?: boolean - Whether to capture key repeat events (default: true)
  • timestampPrecision?: number - Decimal places for timestamps (default: 3)
  • replayEventName?: string - Custom event name for replay events (default: 'keyboardHistoryReplay')

Methods

start(): void

Starts a new keyboard recording session. If already recording, maintains the current session gracefully.

keyboardHistory.start();

stop(): void

Stops the current recording session. Handles gracefully if no session is active.

keyboardHistory.stop();

getRecordedKeys(): KeyEvent[]

Returns all recorded keyboard events from the current session in chronological order.

const events = keyboardHistory.getRecordedKeys();

Returns: Array of KeyEvent objects

isRecording(): boolean

Returns whether a recording session is currently active.

if (keyboardHistory.isRecording()) {
  console.log('Currently recording...');
}

getSession(): RecordingSession

Gets the current session information including state and events.

const session = keyboardHistory.getSession();
console.log('Recording:', session.isRecording);
console.log('Start time:', session.startTime);
console.log('Events count:', session.events.length);

replay(events?: KeyEvent[]): void

Replays keyboard events in chronological order with original timing intervals. Events are dispatched as CustomEvents using document.dispatchEvent().

Parameters:

  • events (optional): Array of KeyEvent objects to replay. If not provided, replays the currently recorded session events.
// Replay recorded events from current session
keyboardHistory.start();
// ... user types ...
keyboardHistory.stop();
keyboardHistory.replay();

// Replay external events from localStorage or other sources
const savedEvents = JSON.parse(localStorage.getItem('keyboardEvents') || '[]');
keyboardHistory.replay(savedEvents);

stopReplay(): void

Stops the current replay process if one is in progress. Handles gracefully if no replay is active.

// Start replay
keyboardHistory.replay();

// Stop replay after 2 seconds
setTimeout(() => {
  keyboardHistory.stopReplay();
}, 2000);

isReplaying(): boolean

Returns whether a replay is currently in progress.

if (keyboardHistory.isReplaying()) {
  console.log('Currently replaying events...');
}

Types

KeyEvent

interface KeyEvent {
  key: string;        // Key identifier (e.g., 'a', 'Enter', 'Shift')
  duration: number;   // Time held in milliseconds
  timestamp: number;  // Unix timestamp when key was pressed
  code: string;       // Physical key code (e.g., 'KeyA', 'Enter')
}

RecordingSession

interface RecordingSession {
  isRecording: boolean;
  startTime: number | null;
  events: KeyEvent[];
}

ReplaySession

interface ReplaySession {
  isReplaying: boolean;
  currentIndex: number;
  startTime: number | null;
  timeoutIds: number[];
}

KeyboardHistoryConfig

interface KeyboardHistoryConfig {
  maxEvents?: number;
  captureRepeats?: boolean;
  timestampPrecision?: number;
  replayEventName?: string;
}

Usage Examples

Basic Recording Session

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

// Start recording
recorder.start();

// Let user type for a while...
setTimeout(() => {
  // Stop and analyze
  recorder.stop();
  
  const events = recorder.getRecordedKeys();
  console.log(`Captured ${events.length} key events`);
  
  // Calculate average key press duration
  const avgDuration = events.reduce((sum, e) => sum + e.duration, 0) / events.length;
  console.log(`Average key press duration: ${avgDuration.toFixed(1)}ms`);
}, 10000);

Analyzing Typing Patterns

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory({ maxEvents: 1000 });

recorder.start();

// After some typing...
recorder.stop();

const events = recorder.getRecordedKeys();

// Find most frequently pressed keys
const keyFrequency = events.reduce((freq, event) => {
  freq[event.key] = (freq[event.key] || 0) + 1;
  return freq;
}, {} as Record<string, number>);

console.log('Key frequency:', keyFrequency);

// Find longest key press
const longestPress = events.reduce((max, event) => 
  event.duration > max.duration ? event : max
);

console.log('Longest key press:', longestPress);

Real-time Event Processing

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

recorder.start();

// Check for new events periodically
setInterval(() => {
  const events = recorder.getRecordedKeys();
  const recentEvents = events.slice(-5); // Last 5 events
  
  console.log('Recent keys:', recentEvents.map(e => e.key).join(''));
}, 1000);

Export Data for Analysis

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

recorder.start();

// After recording session...
recorder.stop();

const session = recorder.getSession();
const events = recorder.getRecordedKeys();

const exportData = {
  metadata: {
    exportTime: new Date().toISOString(),
    totalEvents: events.length,
    sessionStartTime: session.startTime,
    recordingDuration: session.startTime ? performance.now() - session.startTime : 0
  },
  events: events
};

// Convert to JSON for export
const jsonData = JSON.stringify(exportData, null, 2);
console.log('Export data:', jsonData);

Event Replay and Simulation

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

// Record a typing session
recorder.start();
// ... user types "hello world" ...
recorder.stop();

// Set up event listeners to capture replayed events
document.addEventListener('keyboardHistoryReplay', (event) => {
  const customEvent = event as CustomEvent;
  console.log('Replayed key:', customEvent.detail);
});

// Replay the recorded events with original timing
recorder.replay();

// Stop replay after 5 seconds if still running
setTimeout(() => {
  if (recorder.isReplaying()) {
    recorder.stopReplay();
    console.log('Replay stopped');
  }
}, 5000);

Custom Replay Event Names

import { KeyboardHistory } from 'keyboard-history';

// Configure custom event name for replay
const recorder = new KeyboardHistory({
  replayEventName: 'myCustomKeyboardReplay'
});

// Record some events
recorder.start();
// ... user types ...
recorder.stop();

// Listen for the custom event name
document.addEventListener('myCustomKeyboardReplay', (event) => {
  const customEvent = event as CustomEvent;
  console.log('Custom replay event:', customEvent.detail);
  
  // Access event details
  console.log('Key:', customEvent.detail.key);
  console.log('Duration:', customEvent.detail.duration);
  console.log('Original timestamp:', customEvent.detail.originalTimestamp);
  console.log('Replay timestamp:', customEvent.detail.replayTimestamp);
});

// Replay with custom event name
recorder.replay();

External Data Replay

KeyboardHistory supports replaying keyboard events from external data sources like localStorage, databases, or API responses. This is perfect for recreating user sessions, automated testing, or cross-session analysis.

Saving and Loading from localStorage

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

// Record a session
recorder.start();
// ... user types ...
recorder.stop();

// Save events to localStorage
const events = recorder.getRecordedKeys();
localStorage.setItem('keyboardSession', JSON.stringify(events));

// Later, load and replay the saved events
const savedEvents = JSON.parse(localStorage.getItem('keyboardSession') || '[]');
const replayRecorder = new KeyboardHistory();

// Replay the external events
replayRecorder.replay(savedEvents);

Cross-Session Analysis

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

// Function to save session with metadata
function saveSession(sessionName: string) {
  const events = recorder.getRecordedKeys();
  const sessionData = {
    name: sessionName,
    timestamp: Date.now(),
    events: events,
    metadata: {
      totalEvents: events.length,
      duration: events.length > 0 ? events[events.length - 1].timestamp - events[0].timestamp : 0
    }
  };
  
  localStorage.setItem(`session_${sessionName}`, JSON.stringify(sessionData));
}

// Function to load and replay any saved session
function replaySession(sessionName: string) {
  const sessionData = JSON.parse(localStorage.getItem(`session_${sessionName}`) || 'null');
  
  if (sessionData) {
    console.log(`Replaying session: ${sessionData.name}`);
    console.log(`Original session had ${sessionData.metadata.totalEvents} events`);
    
    const replayRecorder = new KeyboardHistory();
    replayRecorder.replay(sessionData.events);
  }
}

// Usage
recorder.start();
// ... user types ...
recorder.stop();
saveSession('user_onboarding');

// Later, replay the onboarding session
replaySession('user_onboarding');

Database Integration Example

import { KeyboardHistory } from 'keyboard-history';

class KeyboardSessionManager {
  private recorder: KeyboardHistory;
  
  constructor() {
    this.recorder = new KeyboardHistory();
  }
  
  // Save session to database (pseudo-code)
  async saveSessionToDatabase(userId: string, sessionName: string) {
    const events = this.recorder.getRecordedKeys();
    
    const sessionData = {
      userId,
      sessionName,
      events,
      createdAt: new Date().toISOString(),
      eventCount: events.length
    };
    
    // Save to your database
    await fetch('/api/keyboard-sessions', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(sessionData)
    });
  }
  
  // Load and replay session from database
  async replaySessionFromDatabase(sessionId: string) {
    const response = await fetch(`/api/keyboard-sessions/${sessionId}`);
    const sessionData = await response.json();
    
    console.log(`Replaying session: ${sessionData.sessionName}`);
    this.recorder.replay(sessionData.events);
  }
}

// Usage
const sessionManager = new KeyboardSessionManager();

// Record and save
sessionManager.recorder.start();
// ... user interaction ...
sessionManager.recorder.stop();
await sessionManager.saveSessionToDatabase('user123', 'feature_test');

// Later, replay from database
await sessionManager.replaySessionFromDatabase('session_456');

Event Validation and Error Handling

import { KeyboardHistory } from 'keyboard-history';

function validateAndReplayEvents(events: any[]) {
  const recorder = new KeyboardHistory();
  
  try {
    // Validate event structure before replay
    const validEvents = events.filter(event => {
      return event && 
             typeof event.key === 'string' &&
             typeof event.duration === 'number' &&
             typeof event.timestamp === 'number' &&
             typeof event.code === 'string';
    });
    
    if (validEvents.length === 0) {
      console.warn('No valid events found to replay');
      return;
    }
    
    if (validEvents.length !== events.length) {
      console.warn(`Filtered out ${events.length - validEvents.length} invalid events`);
    }
    
    console.log(`Replaying ${validEvents.length} valid events`);
    recorder.replay(validEvents);
    
  } catch (error) {
    console.error('Error during replay:', error);
  }
}

// Usage with potentially malformed data
const externalData = JSON.parse(localStorage.getItem('untrustedEvents') || '[]');
validateAndReplayEvents(externalData);

Automated Testing with External Data

import { KeyboardHistory } from 'keyboard-history';

const recorder = new KeyboardHistory();

// Record user interaction for testing
recorder.start();
// ... user performs complex keyboard interaction ...
recorder.stop();

// Save the recorded events for test automation
const testEvents = recorder.getRecordedKeys();
localStorage.setItem('testKeyboardEvents', JSON.stringify(testEvents));

// Later, in automated tests, replay the interaction
const savedEvents = JSON.parse(localStorage.getItem('testKeyboardEvents') || '[]');

// Create new recorder instance for testing
const testRecorder = new KeyboardHistory();

// Set up test assertions
let replayedEventCount = 0;
document.addEventListener('keyboardHistoryReplay', (event) => {
  replayedEventCount++;
  const customEvent = event as CustomEvent;
  console.log('Test replay event:', customEvent.detail);
});

// Replay the saved events directly
testRecorder.replay(savedEvents);

setTimeout(() => {
  console.log(`Replayed ${replayedEventCount} events for testing`);
}, 2000);

Merging Multiple Sessions

import { KeyboardHistory } from 'keyboard-history';

function mergeSessions(sessionNames: string[]): KeyEvent[] {
  const allEvents: KeyEvent[] = [];
  
  sessionNames.forEach(sessionName => {
    const sessionData = localStorage.getItem(`session_${sessionName}`);
    if (sessionData) {
      const session = JSON.parse(sessionData);
      allEvents.push(...session.events);
    }
  });
  
  // Sort by timestamp to maintain chronological order
  return allEvents.sort((a, b) => a.timestamp - b.timestamp);
}

// Merge and replay multiple sessions
const mergedEvents = mergeSessions(['session1', 'session2', 'session3']);
const recorder = new KeyboardHistory();

console.log(`Replaying ${mergedEvents.length} events from merged sessions`);
recorder.replay(mergedEvents);

External Data Replay Use Cases

The enhanced replay() method with external data support enables powerful scenarios for keyboard event analysis and automation:

🔄 Session Persistence

  • Save keyboard sessions to localStorage, databases, or files
  • Resume analysis across browser sessions or page reloads
  • Build libraries of common interaction patterns

🧪 Automated Testing

  • Record real user interactions once, replay them in tests
  • Create regression test suites based on actual user behavior
  • Validate UI responses to specific keyboard input patterns

📊 Cross-Session Analysis

  • Compare typing patterns across different time periods
  • Analyze user behavior changes over multiple sessions
  • Aggregate data from multiple users for statistical analysis

🎯 User Experience Research

  • Replay user sessions to understand interaction patterns
  • Identify common keystroke sequences and timing patterns
  • Study typing behavior under different conditions

🔧 Debugging and Development

  • Reproduce specific user-reported issues by replaying their exact keystrokes
  • Test keyboard event handling with known problematic sequences
  • Validate timing-sensitive keyboard interactions

📈 Performance Analysis

  • Measure and compare keyboard event processing performance
  • Test system behavior under high-frequency keystroke scenarios
  • Validate event handling consistency across different data sets

Demo Page

The package includes an interactive demo page that showcases all library features:

Running the Demo

# Clone the repository
git clone https://github.com/bgbigbrother/KeyboardHystory.git
cd keyboard-history

# Install dependencies
npm install

# Start the demo server
npm run demo

The demo page provides:

  • Start/Stop Controls - Begin and end recording sessions
  • Real-time Display - See captured events as you type
  • Replay Controls - Replay recorded events with original timing
  • Statistics - View event count, session duration, and average key press duration
  • Export Functionality - Download recorded data as JSON
  • Visual Feedback - Clear indication of recording and replay state

Demo Features

  • Interactive keyboard event capture
  • Real-time event display with formatting for special keys
  • Event replay functionality with visual feedback
  • Replay control (start/stop) with progress indication
  • Session statistics and analytics
  • JSON export with metadata
  • Responsive design with clean UI
  • Error handling and user notifications

Demo Instructions

  1. Recording Events:

    • Click "Start Recording" to begin capturing keyboard events
    • Type on your keyboard to see events appear in real-time
    • Click "Stop Recording" to end the session
  2. Replaying Events:

    • After recording some events, click "Replay Events" to simulate them
    • Watch as the recorded keystrokes are replayed with original timing
    • Use "Stop Replay" to halt replay at any time
    • Listen for custom events or observe visual feedback during replay
  3. External Data Scenarios:

    • Export recorded data as JSON and save it to localStorage
    • Import previously saved sessions from localStorage for replay
    • Test the external data replay functionality by:
      • Recording a session and exporting the data
      • Clearing the current session
      • Importing the saved data and replaying it
    • Experiment with merging multiple saved sessions
    • Validate external data handling with malformed or incomplete event objects
  4. Analyzing Data:

    • View real-time statistics including event count and average duration
    • Export recorded data as JSON for further analysis
    • Clear the session to start fresh
    • Compare statistics between different sessions or external data sets

Browser Compatibility

KeyboardHistory is compatible with modern browsers that support:

  • ES6+ JavaScript (ES2015 and later)
  • DOM Event Handling (addEventListener, removeEventListener)
  • Performance API (performance.now() for high-precision timestamps)
  • Keyboard Events (keydown, keyup events)

Supported Browsers

| Browser | Minimum Version | |---------|----------------| | Chrome | 49+ | | Firefox | 45+ | | Safari | 10+ | | Edge | 14+ |

Feature Detection

The library includes built-in feature detection and will gracefully handle unsupported environments:

// The library will automatically detect and adapt to browser capabilities
const recorder = new KeyboardHistory();

if (recorder.isRecording !== undefined) {
  // Browser supports all required features
  recorder.start();
} else {
  console.warn('KeyboardHistory not fully supported in this browser');
}

Development

Setup

# Clone repository
git clone https://github.com/bgbigbrother/KeyboardHystory.git
cd keyboard-history

# Install dependencies
npm install

Available Scripts

# Development
npm run dev          # Start development server with demo
npm run demo         # Start demo page only

# Building
npm run build        # Build library for production
npm run build:lib    # Build library only
npm run build:types  # Generate TypeScript declarations
npm run build:clean  # Clean build directory

# Testing
npm test             # Run all tests
npm run test:watch   # Run tests in watch mode
npm run test:coverage # Run tests with coverage report

# Quality
npm run lint         # Type checking with TypeScript

Project Structure

keyboard-history/
├── src/                 # Source code
│   ├── KeyboardHistory.ts   # Main class
│   ├── EventCapture.ts      # Event capture module
│   ├── EventStore.ts        # Data storage module
│   ├── types.ts             # TypeScript definitions
│   └── index.ts             # Library entry point
├── test/                # Test files
├── demo/                # Demo page
├── dist/                # Built library (generated)
└── docs/                # Documentation

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass (npm test)
  6. Commit your changes (git commit -m 'Add amazing feature')
  7. Push to the branch (git push origin feature/amazing-feature)
  8. Open a Pull Request

License

MIT License - see LICENSE file for details.

Support


Made with ❤️ for the web development community