Quickstart
Install WriteTrack, attach it to a text input, and send captured typing data to your server.
Installation
Section titled “Installation”npm i writetrackpnpm add writetrackyarn add writetrackbun add writetrackCapture Typing Data
Section titled “Capture Typing Data”Attach WriteTrack to any <textarea>, <input>, or contenteditable element:
import { WriteTrack, webhook } from 'writetrack';
const textarea = document.querySelector<HTMLTextAreaElement>('#response-field')!;const tracker = new WriteTrack({ target: textarea });
// Session data is POSTed to your endpoint when getData() is calledtracker.pipe(webhook({ url: 'https://api.example.com/writetrack' }));
// Start recording before the user begins typingtracker.start();Collect and Submit
Section titled “Collect and Submit”Call getData() on form submit — registered sinks deliver the data automatically:
document.getElementById('response-form')!.addEventListener('submit', (e) => { e.preventDefault();
// getData() dispatches to all registered sinks const data = tracker.getData();
tracker.stop();});WriteTrack also ships sinks for Datadog, Segment, and OpenTelemetry. See Output Sinks for details.
getData() returns a structured object with the full session:
{ "version": "2.1.0", "metadata": { "tool": { "name": "writetrack", "version": "0.7.0" }, "targetElement": "textarea", "fieldId": "response-field", "sessionId": "wt_a1b2c3d4e5f6", "timestamp": "2026-02-15T12:00:00.000Z", "duration": 8432 }, "session": { "events": [{ "type": "keydown", "key": "H", "timestamp": 1012.5 }], "clipboardEvents": [], "undoRedoEvents": [], "selectionEvents": [], "programmaticInsertionEvents": [], "compositionEvents": [], "rawText": "Hello world", "sessionStartTime": 1000.0, "sessionEndTime": 9432.0 }, "quality": { "overallScore": 0.85, "completeness": 0.92, "sequenceValid": true, "timingValid": true, "sessionDuration": 8432, "eventCount": 142, "qualityLevel": "GOOD", "issues": [], "validatedAt": "2026-02-15T12:00:08.432Z" }}The quality.qualityLevel field (POOR, FAIR, GOOD, EXCELLENT) reflects whether enough behavioral data was captured for meaningful analysis. Short sessions or sessions with few keystrokes will score lower.
Full HTML Example
Section titled “Full HTML Example”A complete, self-contained page using the browser bundle:
<form id="response-form"> <textarea id="response-field" rows="10" placeholder="Type your response..." ></textarea> <button type="submit">Submit</button></form>
<script type="module"> import { WriteTrack } from 'https://unpkg.com/writetrack/dist/browser/index.js'; import { webhook } from 'https://unpkg.com/writetrack/dist/browser/pipes.js';
const textarea = document.getElementById('response-field'); const tracker = new WriteTrack({ target: textarea }); tracker.pipe(webhook({ url: '/api/writetrack' })); tracker.start();
document.getElementById('response-form').addEventListener('submit', (e) => { e.preventDefault(); tracker.getData(); // dispatches to webhook tracker.stop(); });</script>Cleanup
Section titled “Cleanup”Always stop the tracker when you’re done to remove event listeners:
tracker.stop();Context Fields
Section titled “Context Fields”Tag sessions with identifiers that flow through to getData() output and all sinks:
const tracker = new WriteTrack({ target: textarea, userId: 'u_abc123', contentId: 'post_draft_42', metadata: { formName: 'signup', variant: 'B' },});| Field | Type | Description |
|---|---|---|
userId | string | Who is typing. Appears in metadata.userId. |
contentId | string | What they’re typing into. Appears in metadata.contentId. |
metadata | Record<string, unknown> | Arbitrary key-value pairs. Appears as metadata.custom. |
Analyze the Session (Optional)
Section titled “Analyze the Session (Optional)”If you have a license key (or are running on localhost), you can analyze the captured session for authenticity signals:
import { formatIndicator } from 'writetrack';
const analysis = await tracker.getAnalysis();if (analysis) { console.log(formatIndicator(analysis.contentOrigin.indicator)); console.log(formatIndicator(analysis.timingAuthenticity.indicator));}Or combine capture data with analysis in one call:
const report = await tracker.getSessionReport();// report.data — captured events (same as getData())// report.analysis — authenticity analysis (same as getAnalysis())See the Analysis guide for full details.
Next Steps
Section titled “Next Steps”- Session Persistence — Save and resume sessions across page reloads
- Licensing — License keys for production use
- React Integration —
useWriteTrackhook with automatic lifecycle management - Vue Integration —
useWriteTrackcomposable for Vue 3 - Output Sinks — Route data to webhooks, Datadog, Segment, or OpenTelemetry
- API Reference — Complete method and type reference