Wraps consumer-owned audio or video and applies media commands from external controls.
<div nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range commandfor="example-media" name="volume" style="max-width: 100px"></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="example-media" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="example-media"></nve-media-fullscreen-button>
</div>
</div>
<div nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range commandfor="example-media" name="volume" style="max-width: 100px"></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="example-media" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="example-media"></nve-media-fullscreen-button>
</div>
</div>
Installation
<script type="module">
import '@nvidia-elements/media/controller/define.js';
</script>
<div nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range commandfor="example-media" name="volume" style="max-width: 100px"></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="example-media" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="example-media"></nve-media-fullscreen-button>
</div>
</div>
<script type="module">
import '@nvidia-elements/media/controller/define.js';
</script>
<div nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range commandfor="example-media" name="volume" style="max-width: 100px"></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="example-media" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="example-media"></nve-media-fullscreen-button>
</div>
</div>
nve api.get nve-media-controller
nve api.get nve-media-controller
Usage
nve-media-controller is the command target for a consumer-owned video or audio element. Put the media element in the default slot and keep sources, tracks, posters, captions, and framework bindings on the native element.
<nve-media-controller id="controller">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-controller id="controller">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
Form-associated media controls report values through native FormData. Use this when playback settings should join standard form workflows.
<form id="controller-form" nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="form-example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="form-example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full pad-bottom:md">
<nve-media-seek-button commandfor="form-example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="form-example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="form-example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="form-example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="form-example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="form-example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range
commandfor="form-example-media"
name="volume"
style="max-width: 100px"
></nve-media-volume-range>
<nve-media-playback-rate-select
commandfor="form-example-media"
name="playbackRate"
></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="form-example-media"></nve-media-fullscreen-button>
</div>
<pre></pre>
</form>
<script type="module">
const form = document.querySelector("#controller-form");
const pre = document.querySelector("#controller-form pre");
pre.innerText = JSON.stringify({ currentTime: 0, volume: 1, playbackRate: 1 }, null, 2);
form.addEventListener("input", () => {
const values = Object.fromEntries(new FormData(form).entries());
pre.innerText = JSON.stringify(values, null, 2);
});
</script>
<form id="controller-form" nve-layout="column gap:none" style="width: 550px">
<nve-media-controller id="form-example-media">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-media-time-range commandfor="form-example-media" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full pad-bottom:md">
<nve-media-seek-button commandfor="form-example-media" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="form-example-media" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="form-example-media" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="form-example-media" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="form-example-media" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="form-example-media"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range
commandfor="form-example-media"
name="volume"
style="max-width: 100px"
></nve-media-volume-range>
<nve-media-playback-rate-select
commandfor="form-example-media"
name="playbackRate"
></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="form-example-media"></nve-media-fullscreen-button>
</div>
<pre></pre>
</form>
<script type="module">
const form = document.querySelector("#controller-form");
const pre = document.querySelector("#controller-form pre");
pre.innerText = JSON.stringify({ currentTime: 0, volume: 1, playbackRate: 1 }, null, 2);
form.addEventListener("input", () => {
const values = Object.fromEntries(new FormData(form).entries());
pre.innerText = JSON.stringify(values, null, 2);
});
</script>
Card
Standard video player with playback controls, timeline scrubber, and volume controls. Use for reviewing recorded sensor data, simulation playback, or training video content.
<nve-card style="width: 550px">
<nve-media-controller id="card-controller">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-card-content
role="toolbar"
aria-label="media controls"
style="--padding: var(--nve-ref-size-200) var(--nve-ref-size-400) var(--nve-ref-size-400) var(--nve-ref-size-400)"
>
<nve-media-time-range commandfor="card-controller" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="card-controller" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="card-controller" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="card-controller" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="card-controller" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="card-controller" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="card-controller"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range
commandfor="card-controller"
name="volume"
style="max-width: 100px"
></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="card-controller" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="card-controller"></nve-media-fullscreen-button>
</div>
</nve-card-content>
</nve-card>
<nve-card style="width: 550px">
<nve-media-controller id="card-controller">
<video src="/static/video/particle.mp4" playsinline></video>
</nve-media-controller>
<nve-card-content
role="toolbar"
aria-label="media controls"
style="--padding: var(--nve-ref-size-200) var(--nve-ref-size-400) var(--nve-ref-size-400) var(--nve-ref-size-400)"
>
<nve-media-time-range commandfor="card-controller" name="currentTime"></nve-media-time-range>
<div nve-layout="row gap:xs align:wrap full">
<nve-media-seek-button commandfor="card-controller" action="start"></nve-media-seek-button>
<nve-media-seek-button commandfor="card-controller" action="backward" value="3"></nve-media-seek-button>
<nve-media-pause-button commandfor="card-controller" name="paused" checked value="true"></nve-media-pause-button>
<nve-media-seek-button commandfor="card-controller" action="forward" value="3"></nve-media-seek-button>
<nve-media-seek-button commandfor="card-controller" action="end"></nve-media-seek-button>
<nve-media-mute-button
commandfor="card-controller"
name="muted"
value="true"
style="margin-left: auto"
></nve-media-mute-button>
<nve-media-volume-range
commandfor="card-controller"
name="volume"
style="max-width: 100px"
></nve-media-volume-range>
<nve-media-playback-rate-select commandfor="card-controller" name="playbackRate"></nve-media-playback-rate-select>
<nve-media-fullscreen-button commandfor="card-controller"></nve-media-fullscreen-button>
</div>
</nve-card-content>
</nve-card>
State
Read the latest immutable state from mediaState. The state includes paused, muted, ended, seeking, fullscreen, currentTime, duration, volume, playbackRate, and buffered.
The buffered value contains ordered, disjoint time spans in seconds. The controller copies these spans from the native media element instead of exposing the browser-owned TimeRanges object.
controller.mediaState.buffered;
// [{ start: 0, end: 22 }, { start: 31, end: 48 }]
controller.mediaState.buffered;
// [{ start: 0, end: 22 }, { start: 31, end: 48 }]
The controller dispatches media-state-change when that snapshot changes. The event detail contains the complete state snapshot and does not bubble. Listen on the controller directly.
const controller = document.querySelector('nve-media-controller');
console.log(controller.mediaState);
controller.addEventListener('media-state-change', event => console.log(event.detail));
const controller = document.querySelector('nve-media-controller');
console.log(controller.mediaState);
controller.addEventListener('media-state-change', event => console.log(event.detail));
The controller continues to reflect paused, muted, ended, seeking, fullscreen, current-time, duration, volume, and playback-rate for styling and inspection. Buffered spans remain available through mediaState and media-state-change. The native media element still emits events such as play, pause, progress, timeupdate, durationchange, volumechange, and ratechange.
The controller does not include src, poster, autoplay, track, or caption APIs. Use native media markup for media ownership and compose the Elements controls around it.
Release Status
All elements and features go through 3 phases of stability, pre-release, beta and stable.
pre-release Docs Preview API Documentation Fully Themeable
beta Robust unit test coverages Passed API Review Passed Designer VQA Review Included in library package stable No known outstanding AA WCAG issues No known outstanding performance issues Adapts to different screen/container sizes No breaking API changes for at least 90 days