Track embedded media players in Google Tag Manager
Google Tag Manager for WordPress can report what your visitors do with the media players embedded on your site. Every play, pause, seek and completion becomes a data layer event, so you can build triggers in Google Tag Manager without writing player-specific JavaScript.
The plugin supports twelve players, each with its own option in the Media players group of the Media events section of the plugin settings. Every option is switched off by default.
This page describes GTM4WP 2.0.0 and newer. Players introduced by that release are marked New in 2.0.0 and are not available in the legacy 1.x versions, which supported YouTube, Vimeo and SoundCloud only.
Supported media players
Each player has its own page covering the exact events it fires, what it reports about the media and the limitations worth knowing before you build a tag. Follow the link on the name of the player.
| Player | Option in the plugin settings | Availability |
|---|---|---|
| YouTube | YouTube video events | Deprecated in 2.0.0 |
| Vimeo | Vimeo video events | |
| SoundCloud | Soundcloud events | |
| HTML5 video and audio | HTML5 video and audio events | Experimental |
| Dailymotion | Dailymotion video events | New in 2.0.0, experimental |
| Mixcloud | Mixcloud events | New in 2.0.0, experimental |
| Cloudflare Stream | Cloudflare Stream video events | New in 2.0.0, experimental |
| Wistia | Wistia video events | New in 2.0.0, experimental |
| JW Player | JW Player video events | New in 2.0.0, experimental |
| VideoPress | VideoPress video events | New in 2.0.0, experimental |
| Spotify | Spotify events | New in 2.0.0, experimental |
| Twitch | Twitch events | New in 2.0.0, experimental |
An experimental option works, but its correctness depends on things the plugin cannot check on every site, such as the theme, the embed code or the player API of the 3rd party service. Experimental options are switched off by default and carry a badge in the plugin settings.
A deprecated option still works and is still maintained, but it is superseded and receives no new development. YouTube is deprecated because Google Tag Manager now ships a native YouTube Video trigger of its own, which is the better way to measure YouTube playback.
Switching media tracking on
Open the plugin settings, go to the Media events section and expand the Media players group. Check the option of every player you want to measure, then save. You do not need to change anything else in WordPress. The rest of the work happens in your Google Tag Manager container, where you create triggers on the event names below.
What actually gets loaded
Turning a player on adds one small tracker script of the plugin’s own to your pages. That script is what watches for embeds and pushes the data layer events.
Most players also need a player API from the 3rd party service, and those are handled differently: the tracker looks through the page first and only requests the API once it has actually found an embed of that provider. A page with no Vimeo video on it never contacts Vimeo, and the same holds for every other provider.
That matters for two reasons beyond page weight. A visitor who never sees a given player never has their IP address, browser and referring page handed to that provider. And the saving is real: the seven player APIs together come to roughly 288 KB, of which Mixcloud alone is about 190 KB.
Four players need no API request at all. HTML5 media uses the browser’s own player, JW Player and Wistia bind to a runtime your site already loads, and VideoPress works by listening for messages the player sends.
gtm4wp.mediaPlayerReady
This event fires once for each player on the page, as soon as that player has loaded and can be controlled. A page with several embeds fires it several times, so attach tags to it with care. It carries the details of the media but no playback state.
gtm4wp.mediaPlayerStateChange
This event fires whenever playback changes state. Read the state from the mediaPlayerState variable. The values are unified across players, so play means the same thing whether the visitor started a Vimeo video or a Spotify track. The common values are play, pause, ended, seeked and buffering. Individual players add their own, and the page for each player lists the exact set.
gtm4wp.mediaPlaybackPercentage
This event fires as playback passes each 10 percent mark of the total duration. Read the mark from the mediaPercentage variable. The marks themselves are listed under playback percentage milestones below.
gtm4wp.mediaPlayerEvent
This event fires for everything that is not a state change, such as a change of playback rate, a change of quality, entering full screen, or an error. The name of the event is in mediaPlayerEvent and any accompanying value is in mediaPlayerEventParam. The available names differ by player, because they follow what each player API exposes, so the page for each player lists its own.
gtm4wp.mediaApiReady
This event fires for YouTube only, once the YouTube iframe API has loaded. It carries mediaType and nothing else, because no video is involved at that point. It is also the one media event that does not populate the built-in Video variables, for the same reason.
Data layer variables in every event
| Variable | Contents |
|---|---|
mediaType | The slug of the player, for example vimeo, soundcloud or youtube. The native HTML5 player reports html5media. |
mediaData.id | The ID of the media at the player service. |
mediaData.author | The name of the uploader, where the player API exposes one. YouTube, SoundCloud, Twitch and Dailymotion report it. The other players leave it empty. |
mediaData.title | The title of the media. Some players expose no title and report the media ID instead, and the page for each player says which. |
mediaData.url | The public URL of the media. |
mediaData.duration | The length of the media in seconds. SoundCloud reports milliseconds. |
mediaCurrentTime | The current position of the player in seconds. SoundCloud reports milliseconds. |
Three more variables appear only in the event they belong to: mediaPlayerState in gtm4wp.mediaPlayerStateChange, mediaPercentage in gtm4wp.mediaPlaybackPercentage, and mediaPlayerEvent together with mediaPlayerEventParam in gtm4wp.mediaPlayerEvent.
To read any of these in Google Tag Manager, create a Data Layer Variable whose name is the text in the first column, for example mediaData.title.
Google Tag Manager built-in Video variables
Google Tag Manager ships built-in Video variables: Video Provider, Video URL, Video Title, Video Status, Video Current Time, Video Duration, Video Percent and Video Visible. Google wrote them for its own native YouTube trigger, so they read a set of flat gtm.video keys that the gtm4wp.media events do not use on their own. GTM4WP writes those keys as well, so you can enable the built-in variables and use them on a Custom Event trigger that listens for a gtm4wp.media event.
Every gtm4wp.media event that has a player to describe carries these keys. The one exception is gtm4wp.mediaApiReady, which fires when the provider’s API loads and has no player to report.
GTM4WP translates mediaPlayerState into Video Status as follows:
| mediaPlayerState | Video Status |
|---|---|
play | start |
pause | pause |
buffering | buffering |
ended | complete |
seeked | seek |
| anything else | empty |
On gtm4wp.mediaPlaybackPercentage the status is progress. A state that has no counterpart in Google Tag Manager, such as cued or unstarted on YouTube, reports an empty status.
So do the events that are not playback states at all: player ready, and every gtm4wp.mediaPlayerEvent. That empty value is deliberate rather than a gap. The data layer keeps whatever a previous push put there, so an event that simply left Video Status out would report the status of the last state change, and a volume change would look like a play. Writing an empty value clears it instead.
Video Current Time and Video Duration are always whole seconds, even for the players that report milliseconds in mediaCurrentTime.
Video Visible
The Video Visible variable reports whether the player was on screen at the moment of the event. Google describes it only as true when the video is visible in the viewport and publishes no percentage threshold, so GTM4WP follows that wording rather than inventing one.
Two things are checked. The page itself has to be on screen, so a video playing in a background tab reports false. And the player’s box has to overlap the viewport without being hidden by CSS. A player scrolled halfway off the bottom of the screen still counts as visible.
The background tab half matters more than it first appears. A video keeps playing when the visitor switches tabs, so its progress milestones keep arriving from a player nobody is looking at. Geometry alone would report every one of those as visible.
Two situations cannot be detected and are reported as whatever the page’s own visibility says: a browser window completely covered by another window, which no browser reports to a page, and a video popped out into Picture in Picture, which stays on screen while its tab is hidden.
When there is no player element to measure, the variable is left out of the push entirely rather than guessed at, so it stays undefined instead of reporting a false that was never measured.
Playback percentage milestones
The plugin watches the position of the player and fires gtm4wp.mediaPlaybackPercentage once for each 10 percent mark the visitor passes. The marks are 0, 10, 20, 30, 40, 50, 60, 70, 80 and 90. The mark is in the mediaPercentage variable and in the Video Percent built-in variable.
There is no 100 mark, so measure a completed play with the ended state on gtm4wp.mediaPlayerStateChange instead. The 0 mark fires as soon as the position passes zero, which is a fraction of a second into playback, so it tells you that playback started rather than that nothing was watched.
The plugin reports each mark once per media item. Rewinding and playing the same part again does not fire it a second time.
A player that reports no duration, such as a Twitch live stream or an HTML5 element whose metadata has not loaded yet, fires no milestones.
Players inserted after the page loads
New in 2.0.0. Each tracker wires the players that are in the page when it loads. A player opened in a popup or a lightbox, or loaded by AJAX, arrives later and stays unmeasured. The Track dynamically inserted players option, in the Advanced group of the same settings section, covers those as well, and its own page describes what it costs and when to use it.

