HTML5 video and audio tracking in Google Tag Manager
Google Tag Manager for WordPress reports how your visitors use the native <video> and <audio> players on your site. This is the player WordPress uses for a media file you upload yourself, so it covers self hosted video and audio without involving any 3rd party service.
This page describes GTM4WP 2.0.0 and newer. Features introduced by that release are marked New in 2.0.0. Where the legacy 1.x versions of the plugin behaved differently, the older behavior is marked with a bold GTM4WP 1.x: lead-in.
The option is experimental and carries an Experimental badge in the plugin settings. The reason is what sits between the plugin and the file: the tracker binds to the browser’s own media element, so a theme or a player library that wraps or replaces that element, or a live source with no fixed length, can change which events fire and what they report.
Turning HTML5 media tracking on
Open the plugin settings, go to the Media events section, expand the Media players group and check HTML5 video and audio events. The option is switched off by default.
Which players are detected
The tracker wires every <video> and <audio> element on the page, whatever put it there: the Video and Audio blocks of the block editor, a theme template, a page builder or hand written HTML. The file itself can live in your media library or on any other host.
This is the only player that needs no 3rd party code at all. The browser already provides the API, so the plugin requests nothing from anyone and there is nothing that a consent manager or an ad blocker can prevent from loading.
Embedded players from YouTube, Vimeo, SoundCloud and the other supported services are iframes rather than media elements, so they are never picked up here. Each has its own option and its own tracker.
A player that is in the page when it loads is wired straight away. One inserted later, for example in a popup, a lightbox or a section loaded by AJAX, needs the Track dynamically inserted players option in the Advanced group as well.
The gtm4wp.mediaPlayerReady event waits until the browser has read the metadata of the file, so the duration it reports is the real one rather than a placeholder.
Playback states
The gtm4wp.mediaPlayerStateChange event fires with these values in the mediaPlayerState variable:
| mediaPlayerState | Fires when | Availability |
|---|---|---|
play | Playback has actually started, after any initial buffering. | |
pause | The visitor pauses the file. | |
ended | The file reaches its end. | |
seeked | The visitor jumps to another position. | |
buffering | Playback stops to wait for more data. | New in 2.0.0 |
GTM4WP 1.x: the state was the name of the underlying HTML5 event, passed through as it came. The play state therefore came from the browser’s play event, which fires when playback is requested. It now comes from the playing event, which fires once the file really starts. A file that buffers before it starts reports buffering first and play afterwards. Expect a trigger that counts plays to report slightly fewer, and more accurate, starts after the upgrade.
Player events
The gtm4wp.mediaPlayerEvent event carries the name of the event in the mediaPlayerEvent variable and the accompanying value in mediaPlayerEventParam.
| mediaPlayerEvent | mediaPlayerEventParam | Availability |
|---|---|---|
ratechange | The new playback rate. | |
volumechange | The new volume. | |
error | The numeric error code of the browser’s media error. | |
enterpictureinpicture | true | New in 2.0.0 |
leavepictureinpicture | true | New in 2.0.0 |
fullscreenchange | true when the player entered full screen, false when it left. | New in 2.0.0 |
The last three apply to <video> only. An <audio> element has no picture to put in a corner and nothing to make full screen, so the plugin does not listen for them there.
What mediaData holds for an HTML5 player
A media file carries no metadata about itself the way a hosted service does. There is no title and no uploader to read, so the plugin falls back on the file name, which is the only stable identifier available.
| Variable | Contents |
|---|---|
mediaData.id | The file name, for example interview.mp4. |
mediaData.author | Always empty. A media element exposes no uploader. |
mediaData.title | The file name again, the same value as the ID. |
mediaData.url | The full address of the file the player is actually using, exactly as requested. |
mediaData.duration | The length of the file in seconds, or 0 while the browser does not know it yet. |
The file name is taken from the address with the query string and any fragment removed, so it stays the same file from one report to the next. That matters more than it sounds. A cache busting ?ver= parameter would otherwise turn the same video into a new one after every plugin update, and a signed address from a CDN would turn it into a new one on every single request, besides writing the signature itself into your data layer. The reported url keeps the whole address, because that is what actually played.
The file name is read fresh on every event rather than once at the start. A player that offers several <source> elements only settles on one after the browser has picked a format, and a player whose source is swapped at runtime reports the new file from that point on.
Give your media files descriptive names if you plan to report on them. product-tour-2026.mp4 reads far better in a report than final-v3-edit.mp4.
mediaType and Video Provider disagree
This is the one place in the plugin where the two naming schemes do not match. The mediaType variable is html5media, while the Video Provider built-in variable of Google Tag Manager reports html5 for the same player. Use whichever matches the variable you are reading, and take care if you build a lookup table that mixes the two.
Google Tag Manager built-in Video variables
New in 2.0.0. Every HTML5 media event also populates the built-in Video variables of Google Tag Manager, with Video Provider set to html5 as described above. The section on built-in Video variables explains how to enable and use them. Video Title holds the file name, the same value as mediaData.title, and Video Visible is measured on the media element itself.
Building a trigger in Google Tag Manager
To fire a tag when a visitor starts a self hosted video:
- Create a Data Layer Variable named
mediaPlayerState. - Create a Custom Event trigger whose event name is
gtm4wp.mediaPlayerStateChange. - Add the condition
mediaPlayerStateequalsplayto the trigger. - Attach the trigger to your tag. To identify the file, create a second Data Layer Variable named
mediaData.title.
Use ended in place of play to measure a file that was played to the end. To measure progress, build the trigger on gtm4wp.mediaPlaybackPercentage and add a condition on mediaPercentage instead.
Limitations
- A theme or player library that wraps or replaces the browser’s own media element can change which events fire and what they report. This is the main reason the option is experimental.
- There is no real title and no uploader. The file name stands in for both the ID and the title, and
mediaData.authoris always empty. - Two different files that happen to share a file name report the same title. Progress milestones are counted per full file address, so they stay separate even then.
- Picture in Picture and full screen are reported for
<video>only. - A file whose duration the browser cannot work out, including a live stream with no fixed length, reports a duration of 0 and fires no progress milestones.
- The
errorevent reports the browser’s numeric media error code rather than a message. - There is no 100 percent milestone. Measure a full play with the
endedstate, as described under playback percentage milestones.
Related pages
The overview of media player tracking describes the events, data layer variables and playback milestones that every player shares. See also Cloudflare Stream, whose player follows the same design as the browser’s own, and dynamically inserted players if your media is added to the page after it loads.

