Skip to content

Vanilla JavaScript

WriteTrack works with any HTML page — no framework required. Attach it to a <textarea>, <input>, or contenteditable element and start capturing.

Terminal window
npm i writetrack

On localhost, WriteTrack runs with full analysis enabled — no license key required. For production, register a free trial:

Terminal window
npx writetrack init

The CLI prompts for your email and domain, then saves the key to .env. Pass it when initializing:

const tracker = new WriteTrack({
target: textarea,
license: 'your-license-key',
});

See Licensing for full details.

Attach WriteTrack to any text input and call getData() on submit:

import { WriteTrack } from 'writetrack';
const textarea =
document.querySelector<HTMLTextAreaElement>('#response-field')!;
const tracker = new WriteTrack({ target: textarea });
tracker.start();
document.getElementById('response-form')!.addEventListener('submit', (e) => {
e.preventDefault();
const data = tracker.getData();
console.log('Session quality:', data.quality.qualityLevel);
tracker.stop();
});

Run the WASM analysis engine to assess typing authenticity:

import { WriteTrack, formatIndicator } from 'writetrack';
const tracker = new WriteTrack({ target: textarea });
tracker.start();
// ... user types ...
tracker.stop();
const analysis = await tracker.getAnalysis();
if (analysis) {
console.log(formatIndicator(analysis.contentOrigin.indicator));
// → "All text was typed directly"
console.log(formatIndicator(analysis.timingAuthenticity.indicator));
// → "Timing variability is within normal range"
}

Or bundle raw data and analysis into a single payload with getSessionReport():

const report = await tracker.getSessionReport();
// report.data — captured events (same as getData())
// report.analysis — authenticity analysis (same as getAnalysis())
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify(report),
});

Use .pipe() to route session data to your backend automatically when getData() is called:

import { WriteTrack, webhook } from 'writetrack';
const tracker = new WriteTrack({
target: document.querySelector('#response-field')!,
});
tracker.pipe(webhook({ url: '/api/writetrack' }));
tracker.start();

WriteTrack also ships sinks for Datadog, Segment, and OpenTelemetry. See Output Sinks for details.

WriteTrack works with contenteditable elements the same way:

const editor = document.querySelector<HTMLDivElement>('#editor')!;
const tracker = new WriteTrack({ target: editor });
tracker.start();

Track different fields independently:

const titleTracker = new WriteTrack({
target: document.querySelector('#title')!,
contentId: 'title',
});
const bodyTracker = new WriteTrack({
target: document.querySelector('#body')!,
contentId: 'body',
});
titleTracker.start();
bodyTracker.start();

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' },
});
FieldTypeDescription
userIdstringWho is typing. Appears in metadata.userId.
contentIdstringWhat they’re typing into. Appears in metadata.contentId.
metadataRecord<string, unknown>Arbitrary key-value pairs. Appears as metadata.custom.