keyboard-history
v1.1.4
Published
A browser-based npm package that captures and stores keyboard interaction data including key presses, duration, and timestamps
Maintainers
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-historyQuick 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 demoThe 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
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
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
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
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,keyupevents)
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 installAvailable 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 TypeScriptProject 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/ # DocumentationContributing
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Add tests for new functionality
- Ensure all tests pass (
npm test) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
MIT License - see LICENSE file for details.
Support
Made with ❤️ for the web development community
