Vimeo Player SDK reference — @vimeo/player for embedded video playback in A3 training/resource content
From a3-pluginnpx claudepluginhub trusted-american/marketplace --plugin a3-pluginThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
The official Vimeo Player JavaScript SDK for controlling embedded Vimeo videos. A3 uses this for training videos, resource content, and compliance course playback.
pnpm add @vimeo/player
import Player from '@vimeo/player';
const container = document.getElementById('vimeo-container');
const player = new Player(container, {
id: 123456789, // Vimeo video ID
width: 640, // Player width in px
height: 360, // Player height in px
autoplay: false,
loop: false,
muted: false,
autopause: true, // Pause when another Vimeo player starts
background: false, // Background mode (no controls, loops, muted)
byline: false, // Hide uploader byline
color: '1a73e8', // Player accent color (hex without #)
controls: true,
dnt: false, // Do Not Track
keyboard: true, // Keyboard input
pip: true, // Picture-in-Picture button
playsinline: true, // Inline playback on mobile
portrait: false, // Hide uploader portrait
quality: 'auto', // 'auto' | '4K' | '2K' | '1080p' | '720p' | '540p' | '360p'
responsive: true, // Auto-resize to container
speed: true, // Show speed controls
title: false, // Hide video title
transparent: true, // Transparent background on player
});
const player = new Player(container, {
url: 'https://vimeo.com/123456789',
responsive: true,
});
// If there's already a Vimeo iframe in the DOM
const iframe = document.querySelector<HTMLIFrameElement>('#vimeo-iframe');
const player = new Player(iframe);
All methods return Promises.
// Play
await player.play();
// Pause
await player.pause();
// Check if paused
const paused = await player.getPaused(); // boolean
// Check if video ended
const ended = await player.getEnded(); // boolean
// Seek to time (in seconds)
await player.setCurrentTime(30.5);
const currentTime = await player.getCurrentTime(); // number (seconds)
// Set playback rate
await player.setPlaybackRate(1.5); // 0.5 to 2
const rate = await player.getPlaybackRate();
await player.setVolume(0.75); // 0 to 1
const volume = await player.getVolume();
await player.setMuted(true);
const muted = await player.getMuted();
const title = await player.getVideoTitle();
const duration = await player.getDuration(); // Total seconds
const videoId = await player.getVideoId();
const videoUrl = await player.getVideoUrl();
const width = await player.getVideoWidth();
const height = await player.getVideoHeight();
// Quality
const qualities = await player.getQualities(); // Available qualities
const quality = await player.getQuality(); // Current quality
await player.setQuality('1080p');
const chapters = await player.getChapters();
// => [{ startTime: 0, title: 'Intro', index: 1 }, ...]
const currentChapter = await player.getCurrentChapter();
// Text tracks (subtitles/captions)
const textTracks = await player.getTextTracks();
await player.enableTextTrack('en'); // Enable English captions
await player.disableTextTrack();
// Get buffered percentage
const buffered = await player.getBuffered(); // 0 to 1
// Get played ranges (TimeRanges-like)
const played = await player.getPlayed();
// Get seeking state
const seeking = await player.getSeeking();
// Remove the player and clean up
await player.destroy();
// Container element is emptied
player.on('play', (data) => {
console.log('Playing at', data.seconds, 'of', data.duration);
});
player.on('pause', (data) => {
console.log('Paused at', data.seconds);
});
player.on('ended', (data) => {
console.log('Video ended');
markTrainingComplete();
});
player.on('timeupdate', (data) => {
// Fires ~4 times per second during playback
console.log('Time:', data.seconds);
console.log('Percent:', data.percent); // 0 to 1
console.log('Duration:', data.duration);
updateProgressBar(data.percent);
});
player.on('progress', (data) => {
// Buffering progress
console.log('Buffered:', data.percent);
});
player.on('seeked', (data) => {
console.log('Seeked to', data.seconds);
});
player.on('volumechange', (data) => {
console.log('Volume:', data.volume);
});
player.on('playbackratechange', (data) => {
console.log('Speed:', data.playbackRate);
});
player.on('loaded', (data) => {
console.log('Video loaded, ID:', data.id);
});
player.on('error', (data) => {
console.error('Player error:', data.message);
// data.method — the method that triggered the error
// data.name — error name
});
// Chapter change
player.on('chapterchange', (data) => {
console.log('Chapter:', data.title, 'at', data.startTime);
});
// Quality change
player.on('qualitychange', (data) => {
console.log('Quality changed to:', data.quality);
});
// Remove a specific handler
player.off('timeupdate', myHandler);
// Remove all handlers for an event
player.off('timeupdate');
For responsive layouts, use the responsive: true option and ensure the container has a defined width:
<div id="vimeo-container" style="width: 100%; max-width: 800px;"></div>
const player = new Player(container, {
id: 123456789,
responsive: true,
});
The SDK injects an iframe wrapped in a <div> with padding-bottom to maintain 16:9 aspect ratio.
.vimeo-wrapper {
position: relative;
width: 100%;
padding-bottom: 56.25%; /* 16:9 */
overflow: hidden;
}
.vimeo-wrapper iframe {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
A3 pattern for wrapping the Vimeo player in a Glimmer component:
// app/components/vimeo-player.gts
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { modifier } from 'ember-modifier';
import Player from '@vimeo/player';
interface VimeoPlayerSignature {
Args: {
videoId: number;
onEnded?: () => void;
onProgress?: (percent: number) => void;
autoplay?: boolean;
};
Element: HTMLDivElement;
}
export default class VimeoPlayerComponent extends Component<VimeoPlayerSignature> {
@tracked player: Player | null = null;
@tracked isPlaying = false;
@tracked progress = 0;
setupPlayer = modifier((element: HTMLDivElement) => {
const player = new Player(element, {
id: this.args.videoId,
responsive: true,
autoplay: this.args.autoplay ?? false,
title: false,
byline: false,
portrait: false,
});
this.player = player;
player.on('play', () => {
this.isPlaying = true;
});
player.on('pause', () => {
this.isPlaying = false;
});
player.on('timeupdate', (data) => {
this.progress = data.percent;
this.args.onProgress?.(data.percent);
});
player.on('ended', () => {
this.isPlaying = false;
this.args.onEnded?.();
});
// Cleanup on element destruction
return () => {
player.destroy();
};
});
@action async togglePlay() {
if (!this.player) return;
const paused = await this.player.getPaused();
if (paused) {
await this.player.play();
} else {
await this.player.pause();
}
}
}
<VimeoPlayer
@videoId={{12345}}
@onEnded={{this.markComplete}}
@onProgress={{this.trackProgress}}
/>
A3 tracks training video completion by monitoring playback progress:
// Track watched segments to prevent skipping
const watchedSegments: Set<number> = new Set();
player.on('timeupdate', (data) => {
// Record each second watched
const second = Math.floor(data.seconds);
watchedSegments.add(second);
// Calculate actual watch percentage (not just seek position)
const totalSeconds = Math.floor(data.duration);
const watchedPercent = watchedSegments.size / totalSeconds;
if (watchedPercent >= 0.9) {
// User has watched at least 90% of unique content
markVideoComplete(videoId);
}
});
// Change the video without destroying the player
await player.loadVideo(newVideoId);
// or
await player.loadVideo('https://vimeo.com/987654321');
// The 'loaded' event fires when the new video is ready
player.on('loaded', (data) => {
console.log('New video loaded:', data.id);
});
const player = new Player(container, {
id: videoId,
dnt: true, // Do Not Track — disables tracking cookies and analytics
});
For private/domain-restricted videos, ensure the embedding domain is whitelisted in Vimeo video settings.
autopause: true (default) pauses other Vimeo players when one starts. Set to false if you need simultaneous playback.muted: true with autoplay: true for mobile-safe autoplay.responsive: true and height. The responsive wrapper uses padding-based scaling that conflicts with fixed dimensions.