Lightweight timeline editor web component for animations, video editing, and sequencing. Zero dependencies.
npm install @andymcloid/trakk
Lightweight timeline editor web component
Zero dependencies. Works everywhere.
Installation •
Usage •
API •
Events •
Styling •
Demo

---
A timeline editor for building animation tools, video editors, audio sequencers, or anything that needs time-based sequencing. Built as a native Web Component with zero dependencies.
``bash`
npm install @andymcloid/trakk
Or use directly via CDN:
`html`
`html
`
| Method | Description |
|--------|-------------|
| setData(tracks) | Set timeline data |play(options?)
| | Start playback. Options: { autoEnd: boolean, toTime: number } |pause()
| | Pause playback |setTime(time)
| | Set current time position |getTime()
| | Get current time |getTotalTime()
| | Get total duration (end of last block) |setConfig(config)
| | Update configuration |selectAction(action, row?)
| | Select a block programmatically |deselectAction()
| | Clear the current selection |getSelectedAction()
| | Get currently selected block { action, row } or null |saveToLocalStorage(key?)
| | Save to localStorage |loadFromLocalStorage(key?)
| | Load from localStorage |
`javascript`
timeline.setConfig({
scale: 1, // Seconds per scale unit
scaleWidth: 160, // Pixels per scale unit
scaleCount: 20, // Number of scale units
startLeft: 100, // Left margin (label column width)
rowHeight: 32, // Track height in pixels
autoScroll: true, // Auto-scroll during playback
hideCursor: false, // Hide playhead cursor
disableDrag: false, // Disable all dragging
allowOverlap: false // Allow blocks to overlap (default: false)
});
`typescript
interface Track {
id: string;
name?: string;
locked?: boolean; // Prevent editing
blocks: Block[];
}
interface Block {
id: string;
name?: string;
start: number; // Start time in seconds
end: number; // End time in seconds
locked?: boolean; // Prevent editing this block
}
`
`javascript
// Data changed (drag, resize, create, delete)
timeline.addEventListener('change', (e) => {
console.log(e.detail.tracks);
});
// New block created via drag
timeline.addEventListener('itemcreated', (e) => {
console.log(e.detail.item, e.detail.row);
});
// Block deleted
timeline.addEventListener('blockdeleted', (e) => {
console.log(e.detail.block, e.detail.track);
});
// Track deleted
timeline.addEventListener('trackdeleted', (e) => {
console.log(e.detail.track);
});
// Block selected/deselected
timeline.addEventListener('select', (e) => {
if (e.detail) {
console.log('Selected:', e.detail.action, 'in track:', e.detail.row);
} else {
console.log('Selection cleared');
}
});
// Track/block renamed (double-click to edit)
timeline.addEventListener('trackrenamed', (e) => {
console.log(e.detail.track, e.detail.name);
});
timeline.addEventListener('blockrenamed', (e) => {
console.log(e.detail.block, e.detail.name);
});
`
Access the playback engine directly:
`javascript
timeline.engine.on('play', () => console.log('Playing'));
timeline.engine.on('paused', () => console.log('Paused'));
timeline.engine.on('ended', () => console.log('Ended'));
timeline.engine.on('setTimeByTick', ({ time }) => {
// Called every frame during playback
console.log('Current time:', time);
});
`
For advanced control over interactions:
`javascript${block.name}
timeline.setCallbacks({
// Custom rendering
getActionRender: (block, track) => ,${time.toFixed(1)}s
getScaleRender: (time) => ,
// Interaction hooks (return false to cancel)
onActionMoving: ({ action, start, end }) => {
if (start < 0) return false; // Prevent moving before 0
},
onActionResizing: ({ action, start, end }) => {
if (end - start < 0.5) return false; // Minimum 0.5s duration
},
// Click handlers (button: 0=left, 1=middle, 2=right)
onClickAction: (e, { action, row, time, button }) => {},
onDoubleClickAction: (e, { action, row, time }) => {},
onContextMenuAction: (e, { action, row, time, button }) => {
// Right-click on block - show custom context menu
},
onClickRow: (e, { row, time }) => {},
onClickTimeArea: (e, { time }) => {}
});
`
Customize Trakk's appearance using CSS custom properties:
`css
trakk-editor {
/ Core colors /
--trakk-bg: #191b1d; / Background color /
--trakk-text: #ffffff; / Text color /
--trakk-accent: #5297FF; / Accent color (cursor, selection border) /
/ Block colors /
--trakk-block-bg: #2f3134; / Block background /
--trakk-block-bg-hover: #3a3d40; / Block hover state /
--trakk-block-bg-selected: #4a7ba7; / Selected block background /
/ Borders /
--trakk-border: rgba(255, 255, 255, 0.1); / Subtle borders /
--trakk-border-strong: rgba(255, 255, 255, 0.3); / Prominent borders /
/ Text variations /
--trakk-text-muted: rgba(255, 255, 255, 0.6); / Secondary text /
--trakk-text-subtle: rgba(255, 255, 255, 0.4); / Subtle text/icons /
/ State /
--trakk-danger: #ff6464; / Delete button hover /
--trakk-disabled-opacity: 0.6; / Disabled elements /
--trakk-locked-opacity: 0.7; / Locked elements /
}
`
`css`
trakk-editor.light {
--trakk-bg: #f5f5f5;
--trakk-text: #1a1a1a;
--trakk-accent: #0066cc;
--trakk-block-bg: #e0e0e0;
--trakk-block-bg-hover: #d0d0d0;
--trakk-block-bg-selected: #b3d4fc;
--trakk-border: rgba(0, 0, 0, 0.1);
--trakk-border-strong: rgba(0, 0, 0, 0.2);
--trakk-text-muted: rgba(0, 0, 0, 0.6);
--trakk-text-subtle: rgba(0, 0, 0, 0.4);
}
Open demo.html` in a browser or check out the live demo.
MIT