peekbar
v0.1.0
Published
Hover frame previews on HTML5 video seekbars, generated in the browser for short videos
Downloads
177
Maintainers
Readme
peekbar
Hover frame previews on any HTML5 video seekbar. Generates the sprite sheet in the browser with Canvas: no server, no ffmpeg. Best for short videos. Optional spriteUrl if you already have a sheet.
Features
- Zero dependencies
- Client-side sprite generation for short videos
- Optional
spriteUrlto skip canvas generation - Offscreen clone so the visible player doesn't seek or flicker
destroy()for cleanup (React/Vue unmounts, cancel mid-generation)- Touch + mouse scrubbing
- TypeScript types included
Installation
npm install peekbarQuick Start
import { peekbar } from 'peekbar';
const video = document.querySelector('video');
const seekbar = document.querySelector('.my-seekbar');
const destroy = peekbar(video, seekbar, {
interval: 5,
thumbWidth: 160,
thumbHeight: 90,
onProgress: (current, total) => {
console.log(`Generating... ${Math.round((current / total) * 100)}%`);
},
onReady: () => {
console.log('Previews ready');
}
});
// Cleanup when tearing down
destroy();Demo: peekbar.pushkaraj.me
TypeScript
import { peekbar, PeekbarOptions } from 'peekbar';
const options: PeekbarOptions = {
interval: 10,
thumbWidth: 160,
thumbHeight: 90,
cols: 8,
spriteUrl: 'https://cdn.example.com/sprite.jpg'
};
const video = document.getElementById('my-video') as HTMLVideoElement;
const seekbar = document.getElementById('my-seekbar') as HTMLElement;
const destroy: () => void = peekbar(video, seekbar, options);API
peekbar(videoEl, progressBarEl, options?)
| Param | Type | Description |
| :--- | :--- | :--- |
| videoEl | HTMLVideoElement | Video to read frames / duration from |
| progressBarEl | HTMLElement | Seekbar that receives hover/touch |
| options | PeekbarOptions | Optional settings |
Options
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| interval | number | 5 | Seconds between captured frames |
| thumbWidth | number | 160 | Thumbnail width (CSS px) |
| thumbHeight | number | 90 | Thumbnail height (CSS px) |
| cols | number | 5 | Columns in the sprite grid |
| offset | number | 12 | Gap (px) between popup and seekbar top |
| quality | number | 0.92 | JPEG quality 0 to 1 (client-side generation only) |
| onProgress | function | | (currentFrame, totalFrames) => {} during generation |
| onReady | function | | Called when previews are active |
| spriteUrl | string | | Prebuilt sprite; skips client-side generation |
Sprites are rendered at up to 2× device pixel ratio so previews stay sharp on retina screens. For larger previews, raise thumbWidth / thumbHeight (e.g. 240 × 135).
Returns
destroy: () => void cancels generation, removes the popup, and detaches listeners.
Styling
Override .peekbar-popup:
.peekbar-popup {
border-radius: 8px !important;
border: 2px solid #6366f1 !important;
box-shadow: 0 10px 25px rgba(99, 102, 241, 0.4) !important;
}SPA recipes
React
import { useEffect, useRef } from 'react';
import { peekbar } from 'peekbar';
function CustomPlayer() {
const videoRef = useRef(null);
const seekbarRef = useRef(null);
useEffect(() => {
if (!videoRef.current || !seekbarRef.current) return;
const destroy = peekbar(videoRef.current, seekbarRef.current, {
interval: 5,
thumbWidth: 160,
thumbHeight: 90
});
return () => destroy();
}, []);
return (
<div>
<video ref={videoRef} src="video.mp4" />
<div ref={seekbarRef} className="seekbar" />
</div>
);
}Vue 3
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { peekbar } from 'peekbar';
const videoEl = ref(null);
const seekbarEl = ref(null);
let destroyFn = null;
onMounted(() => {
destroyFn = peekbar(videoEl.value, seekbarEl.value, {
interval: 5,
thumbWidth: 160,
thumbHeight: 90
});
});
onUnmounted(() => {
if (destroyFn) destroyFn();
});
</script>
<template>
<div>
<video ref="videoEl" src="video.mp4" />
<div ref="seekbarEl" class="seekbar" />
</div>
</template>Development
git clone https://github.com/thepushkaraj/peekbar.git
cd peekbar
npm install
npm test
npm run buildServe the repo root and open examples/basic/index.html for the local demo.
Notes
- Cross-origin videos need CORS headers and
crossoriginon<video>, or use a local file /spriteUrl. - Best for short clips; for long videos prefer a prebuilt
spriteUrl.
License
MIT
