Audio and video
Consistent playback controls, captions, chapters, and transcripts in any Astro site.
Listen or watch
Audio and Video share one control system. Both offer play/pause, a buffered timeline, elapsed and total time, skip buttons, volume, speed, and repeat. Video adds caption selection, fullscreen, picture-in-picture, and AirPlay where the browser supports them.
Three tones
Settings
Transcript
0:00 — 220 Hz. 0:10 — 330 Hz. 0:20 — 440 Hz, fading out at the end.
Wave study
Settings
Transcript
Blue waves drift across the frame. A low tone plays for ten seconds, rises to a middle tone, then rises again for the final ten seconds and fades out.
These files are generated locally by scripts/generate-media-fixtures.mjs. They are teaching samples, not recordings. Explore all parameters in the Audio and Video playgrounds.
Add a player
Import either component directly. It brings its scoped styles and browser behavior; you do not need Alkemist’s Layout, full theme, or Astro integration.
---
import Audio from '@alkemdotdev/alkemist-components/audio';
import Video from '@alkemdotdev/alkemist-components/video';
---
<Audio src="/recordings/interview.mp3" title="Interview" />
<Video
src="/films/experiment.mp4"
poster="/films/experiment.jpg"
title="Experiment"
/>The same imports work in MDX. Ordinary HTML <audio controls> and <video controls> retain their native controls; Markdown integration does not silently replace them. Use the components when you want the shared control system.
Captions, chapters, and transcripts
Supply WebVTT files through tracks. Captions describe speech and meaningful sound; subtitles provide translated or transcribed dialogue. srclang is the language code, and label is the language shown in the menu. A default caption track opens automatically.
<Video
src="/films/experiment.mp4"
title="Experiment walkthrough"
tracks={[
{
src: '/films/experiment.en.vtt',
kind: 'captions',
srclang: 'en',
label: 'English',
default: true,
},
{
src: '/films/experiment.es.vtt',
kind: 'subtitles',
srclang: 'es',
label: 'Español',
},
{
src: '/films/experiment.chapters.vtt',
kind: 'chapters',
srclang: 'en',
label: 'Chapters',
},
]}
transcript="A plain-text description of the experiment and its narration."
/>A chapter track uses the same timestamped WebVTT format, with a short chapter name as each cue. The chapter selector seeks to the chosen section. The optional transcript is a separate, expandable text alternative; captions and transcripts must be authored, they are not generated by the player.
WEBVTT
00:00:00.000 --> 00:00:10.000
Preparation
00:00:10.000 --> 00:00:30.000
The experimentHost track files with the media or allow cross-origin access from your media server. The components request anonymous CORS when tracks are present, so cross-origin media must permit it too.
Configure the controls
| Prop | Default | Purpose |
|---|---|---|
src | Required | Browser-playable audio or video file. |
title | Empty | Visible title and accessible player name. |
caption | Empty | Supporting text below the player. |
poster | Empty | Video image shown before playback. |
preload | metadata | none, metadata, or auto; a hint to the browser. |
autoplay | false | Request automatic playback, subject to browser policy. |
muted | false | Start without sound. |
loop | false | Repeat when playback ends; also adjustable in the controls. |
seekOffset | 10 | Seconds moved by skip buttons and seek shortcuts. |
playbackRates | [0.5, 0.75, 1, 1.25, 1.5, 2] | Speeds offered by the player. |
tracks | [] | Caption, subtitle, and chapter files. |
download | false | Show a link to the original media file. |
transcript | Empty | Plain-text transcript in an expandable section. |
Downloads follow browser and server rules. A cross-origin file may open instead of downloading unless its server sends an attachment response.
The player follows the host’s Alkemist paper, ink, rule, accent, and font tokens, with standalone fallbacks. Scoped media styles keep its controls independent of ordinary page buttons and inputs.
Deliver seekable files
The media server must support HTTP byte ranges (206 Partial Content) for reliable seeking. Correct Content-Type, Content-Length, and CORS headers matter as much as the player controls. A server that ignores Range requests can play from the beginning while failing to jump to a chapter.
Cloudflare Pages currently returns 200 for static-file range requests. This demo adds a narrowly scoped Pages Function for its four small generated media files; all other routes remain static. For a media library or large recordings, use storage or a media CDN that supports byte ranges. This server requirement applies equally to native browser controls. Cloudflare Pages serving behavior
Keyboard and browser behavior
Tab reaches each control; Enter or Space activates buttons, and arrow keys adjust focused sliders. Media shortcuts apply while the player is focused, so they do not capture typing elsewhere. Controls remain visible and wrap on smaller screens.
The underlying media element retains native controls until the custom controls load successfully. If JavaScript is unavailable, the browser player remains usable. Unsupported platform features are hidden; picture-in-picture, fullscreen, and AirPlay depend on the device and browser. Autoplay with sound may be blocked. Source and track failures are reported beside the player.
This release supports browser-playable files. It does not add streaming manifests, DRM, adaptive quality, casting sessions, YouTube, or Vimeo providers. Those require a separate playback adapter.
Design references
The control layer uses Media Chrome, whose web components compose around a standard media element. Its audio layout and controller contract informed the shared structure. Plyr provided a second reference for compact controls and focused keyboard behavior. Alkemist supplies the Astro API, styling, fallback, transcript, chapter navigation, and live examples.