• Courses
    • Courses
    • Course bundles
  • Blog
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
    • GA4 community
  • Services
  • About
    • About
    • Contact
  • Login
  • Courses
    • Courses
    • Course bundles
  • Blog
  • Resources
    • Youtube channel
    • E-books and Guides
    • GTM Recipes
    • View All Resources
    • GTM Community
    • GA4 community
  • Services
  • About
    • About
    • Contact
  • Login

March 4, 2024

How to Track HTML5 Audio Player with Google Tag Manager and GA

Updated: March 4th, 2024

I guess that the majority of the GTM community is familiar with a custom event listener who probably already has legendary status. Even this Twitter (that I saw earlier in my feed) confirmed this:

Yes, I’m talking about David Vallejo’s HTML5 video listener that tracks almost any other video player that is not YouTube or Vimeo (or anything else in the iFrame). You can also find a GTM recipe here.

But today’s blog post is not about the video.

Subscribe and Get the Ebook - Mastering GA4 event tracking

Table of Contents

Here’s what you will learn in this article

  • Media events
  • HTML5 audio listener for GTM (a.k.a. one word can make a difference)
  • Requirements for this solution to work
  • So, how do we actually track the HTML5 audio player with GTM?
    • #1. Custom HTML tag
      • #1.1. Create a Custom JavaScript variable
      • #1.2. Create a trigger that activates the listener only when the audio player is present.
    • #2. Custom Event Trigger and Data Layer Variables
    • #3. Google Analytics event tag
    • #4. Let’s test
  • How to see the data in GA4
  • Google Tag Manager Recipe
  • How to track HTML5 audio player with Google Tag Manager: Final words

 

Media events

Some time ago, I discovered Media Events (I’m still at the beginning of my JavaScript journey, so pardon my ignorance). It turned out that there are a bunch of interactions that can be tracked (e.g., play, pause, etc.) on video players (and audio as well). David’s listener catches those exact events and pushes them to the Data Layer.

But his listener does that only for HTML5 video players. Audio is not supported.

Unless… you change one word in his code. Literally, one word.

P.S. In the final result, more words will be changed (but it was done to match the proper naming convention). But to just start listening to audio events, it’s enough to change one word in David’s code.

Subscribe and Get the Ebook - Real Book Img - GTM for Beginners

 

HTML5 audio listener for GTM (a.k.a. one word can make a difference)

Let’s look at David’s listener code. Since there are no numbers of code lines, you should be looking somewhere at the 60% mark of his code

Spot this line:

var videos = document.getElementsByTagName('video');

The code mentioned above is looking for elements using the HTML tag <video> and will start listening to their events. If we go back to the MDN’s documentation about media events, you’ll see that those interactions are supported by <video> and <audio> elements.

So, the one word that we need to change is video.

That is the minimum you need to do to adapt the listener to HTML5 audio. But to keep things tidy and to have the proper naming convention, I changed more words (for example, I’ve replaced all other “video” words in the code and changed it to “audio”).

So, the final result of the HTML5 audio listener for Google Tag Manager is this:

<script>
// This listener is based on David Vallejo's HTML5 video listener, thyngster.com/tracking-html5-videos-gtm/
// Edited by Julius Fedorovicius, analyticsmania.com

  // Let's wrap everything inside a function so variables are not defined as globals 

(function() {
    // This is gonna our percent buckets ( 10%-90% ) 
    var divisor = 10;
    // We're going to save our players status on this object. 
    var audios_status = {};
    // This is the funcion that is gonna handle the event sent by the player listeners 
    function eventHandler(e) {
        switch (e.type) {
            // This event type is sent everytime the player updated it's current time, 
            // We're using for the % of the audio played. 
        case 'timeupdate':
            // Let's set the save the current player's audio time in our status object 
            audios_status[e.target.id].current = Math.round(e.target.currentTime);
            // We just want to send the percent events once 
            var pct = Math.floor(100 * audios_status[e.target.id].current / e.target.duration);
            for (var j in audios_status[e.target.id]._progress_markers) {
                if (pct >= j && j > audios_status[e.target.id].greatest_marker) {
                    audios_status[e.target.id].greatest_marker = j;
                }
            }
            // current bucket hasn't been already sent to GA?, let's push it to GTM
            if (audios_status[e.target.id].greatest_marker && !audios_status[e.target.id]._progress_markers[audios_status[e.target.id].greatest_marker]) {
                audios_status[e.target.id]._progress_markers[audios_status[e.target.id].greatest_marker] = true;
                dataLayer.push({
                    'event': 'audio',
                    'audioPlayerAction': 'Progress %' + audios_status[e.target.id].greatest_marker,
                    // We are using sanitizing the current audio src string, and getting just the audio name part
                    'audioTitle': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
                });
            }
            break;
            // This event is fired when user's click on the play button
        case 'play':
            dataLayer.push({
                'event': 'audio',
                'audioPlayerAction': 'play',
                'audioTitle': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
            });
            break;
            // This event is fied when user's click on the pause button
        case 'pause':
            dataLayer.push({
                'event': 'audio',
                'audioPlayerAction': 'pause',
                'audioTitle': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1]),
                'audioValue': audios_status[e.target.id].current
            });
            break;
            // If the user ends playing the audio, an Finish audio will be pushed ( This equals to % played = 100 )  
        case 'ended':
            dataLayer.push({
                'event': 'audio',
                'audioPlayerAction': 'finished',
                'audioTitle': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
            });
            break;
        default:
            break;
        }
    }
    // We need to configure the listeners
    // Let's grab all the the "audio" objects on the current page     
    var audios = document.getElementsByTagName('audio');
    for (var i = 0; i < audios.length; i++) {
        // In order to have some id to reference in our status object, we are adding an id to the audio objects
        // that don't have an id attribute. 
        var audioTagId;
        if (!audios[i].getAttribute('id')) {
            // Generate a random alphanumeric string to use is as the id
            audioTagId = 'html5_audio_' + Math.random().toString(36).slice(2);
            audios[i].setAttribute('id', audioTagId);
        }// Current audio has alredy a id attribute, then let's use it
        else {
            audioTagId = audios[i].getAttribute('id');
        }
        // audio Status Object declaration  
        audios_status[audioTagId] = {};
        // We'll save the highest percent mark played by the user in the current audio.
        audios_status[audioTagId].greatest_marker = 0;
        // Let's set the progress markers, so we can know afterwards which ones have been already sent.
        audios_status[audioTagId]._progress_markers = {};
        for (j = 0; j < 100; j++) {
            audios_status[audioTagId].progress_point = divisor * Math.floor(j / divisor);
            audios_status[audioTagId]._progress_markers[audios_status[audioTagId].progress_point] = false;
        }
        // On page DOM, all players currentTime is 0 
        audios_status[audioTagId].current = 0;
        // Now we're setting the event listeners. 
        audios[i].addEventListener("play", eventHandler, false);
        audios[i].addEventListener("pause", eventHandler, false);
        audios[i].addEventListener("ended", eventHandler, false);
        audios[i].addEventListener("timeupdate", eventHandler, false);
    }
})();
</script>

 

Requirements for this solution to work

This listener supports only those audio players embedded on a page that:

  • Are not in the iFrame
  • Use the <audio> HTML tag

You must meet both these conditions for this solution to work. If the listener is indeed not working (and you don’t see the audio events in GTM’s preview and debug mode, inspect the player, and most likely, you will see that there is no <audio> tag on a page or that you’re dealing with an iframe.

As for the audio player, just open your browser’s developer tools, go to the “Elements” tab (a.k.a. “DOM Inspector”), and do a quick search (e.g., CTRL + F). In the search field, enter “<audio” (without quotation marks, and I intentionally skipped the > sign). If it returns 0 results, you’re out of luck.

In fact, it is quite common for audio players to be built by not using the <audio> tag. At least, that’s what I’ve noticed. So if your website utilizes some way-too-fancy audio player, this guide is not for you, sorry.

As for the iFrame, you should inspect the audio player, climb the element tree (DOM tree), and see if there isn’t a <iframe> somewhere along the way.

Subscribe and Get the Ebook - working with reports in ga4

So, how do we actually track the HTML5 audio player with GTM?

Compared to HTML5 video tracking, the process of tracking the audio player is quite the same:

  1. Implement the HTML5 audio player listener (with a Custom HTML tag)
  2. Create Custom Event Trigger and Data Layer Variables
  3. Create a Google Analytics tag to send the audio player data to GA
  4. Test

 

#1. Custom HTML tag

First, we must activate the HTML5 audio player listener on a page. Since this is a custom solution, you must use a Custom HTML tag. So go to GTM > Tags > New > Custom HTML and paste the above code.

When should you activate this listener? There are two possible answers:

  • If you have A LOT of pages with audio players, you can set that Custom HTML tag to fire on the DOM Ready trigger (you’ll need to create that yourself).
  • Suppose you have a listener just on certain pages. In that case, we can follow the usual practice of activating the Listener only on those pages where audio players exist (a.k.a. HTML element <audio> is present).
Subscribe and Get the Ebook - Real Book Img - GTM for Beginners

 

#1.1. Create a Custom JavaScript variable

It will return true if <audio> element is present on a page. If not, then you’ll get false. This Custom Javascript variable and the value it records decide whether to activate the Custom HTML tag (listener).

In GTM, go to Variables > Scroll down to User-defined Variables > Custom JavaScript and paste the following code:

function() {
  return (document.getElementsByTagName('audio').length > 0) ? true : false;
}

We can name the variable cjs – audio player is on a page

 

#1.2. Create a trigger that activates the listener only when the audio player is present.

In Google Tag Manager, go to Triggers > New > DOM Ready. We want it to fire not on all DOM Ready events but on those when the previously created cjs – audio player is on a page variable and returns true.

In other words, only if the HTML5 audio player is actually on a page will the Custom HTML tag be activated.

Assign this trigger to the Custom HTML tag.

 

#2. Custom Event Trigger and Data Layer Variables

After you create the HTML tag (with the listener code) and add a trigger to it, save everything, enable the Preview and Debug mode and go to the website you’re working on (I mean, where the audio player is present).

Refresh that page (the Preview and Debug mode should appear).

Click the DOM Ready event on the left side and check whether the Custom HTML tag has fired. If yes, that’s good. You should expect this. If the Custom HTML tag has fired, the audio player is present. Once you start interacting with the player, new events will appear in the Preview and Debug panel.

Those events are possible because the listener is doing its job; it listens to player interactions and then sends them to the Data Layer so that GTM can be informed.

Anyway, once the Custom HTML tag has fired, click Play in the audio player; you should see a new audio event in the Preview and Debug mode, then click Pause.

If you let the player play, you’ll see even more events (e.g., every 10% of the audio length, a new event will be pushed. Also, pause, play, and finished events are supported).

Click any of the audio events and go to the Data Layer tab. You’ll see helpful data related to that audio player, like audioPlayerAction and audioTitle.

Even though this data is visible in the Preview mode, you cannot use them as triggers or variables unless you specifically instruct GTM to do so. You can do this by creating the Custom Event trigger and several Data Layer Variables. So let’s do that.

Go to Triggers > New > Custom event and enter the following settings.

Then, let’s create two Data Layer Variables. Remember, those settings are case-sensitive.

 

I didn’t create the audioValue variable because I don’t use it (but you can). It returns the threshold (in seconds) of where the video was paused (at least, I think).

 

#3. Google Analytics event tag

In the next step, let’s send the audio player event data to Google Analytics. In GTM, go to Tags > New > Google Analytics: GA4 Event and enter the following settings:

If you have given different names to Data Layer Variables, then enter those exact names in the GA event tag’s settings.

In the triggering section of the tag, click anywhere and add the Custom Event trigger we’ve created in the previous chapter of this blog post.

 

#4. Let’s test

So here’s what was done so far:

  • A Custom HTML tag was created that contains the HTML5 audio player listener. I will fire either on all pages or only on those where the audio player is present (thanks to the Custom JS variable). It’s up to you which option to choose.
  • A Custom Event Trigger and Data Layer Variables were created to catch everything the audio player listener pushes to the Data Layer.
  • A GA4 Event tag was created to fire every time the audio player listener dispatches an event called audio. That event tag will transfer the values of audioPlayerAction and audioTitle.

Enable the GTM Preview and Debug mode, and refresh the page with the audio player.

Start interacting with the player, e.g., clicking the Play button. The expected result:

  • audio event will appear on the left side of the Preview and Debug mode
  • Click it, and you should see the fired GA Event tag

If everything is good, go to your GA4 Real-time reports > Events. Check whether those events are also visible there. If not, read this guide for some ideas on how to troubleshoot.

That’s it! You’re good to go with this solution. Remember, if you don’t see audio events in the Preview and Debug mode, your player is likely not built with the <audio> HTML element or is in the iFrame. Then this guide will not help you.

However, if your player is not in the iFrame and uses the <audio> tag, you could try changing the Listener’s trigger from DOM Ready to Window Loaded.

 

How to see the data in GA4

Since we’re sending two custom parameters when the GA4 event tag fires, we will want to see this information inside GA4. Passing the information to GA4 can be useful, especially when creating custom Exploration reports. If you don’t know how to create Exploration reports, you should read this article.

Moving on – to pass these custom parameters along with the event data to GA4, you must register them as custom dimensions inside GA4. Doing this is fairly straightforward; all you need to do is follow the steps below:

  1. Go to your GA4 property and navigate to Admin > Data Display > Custom Definitions
  2. In the new screen, click on Create Custom Dimension
  3. Add the correct event parameter details (follow the screenshots below) and hit Save. P.S. In the Dimension name field, you can enter anything you want, e.g. “Audio Player action”. This time, I entered audio_player_action.

Your custom dimensions can now be used when creating custom reports.

 

Google Tag Manager Recipe

To save time, here is a ready-made GTM recipe. Click this link, download the file, follow the instructions and start tracking.

 

How to track HTML5 audio player with Google Tag Manager: Final words

There you have it. With a minor tweak in the existing solution, we started tracking different interactions on a page using an HTML5 audio player. This wouldn’t have been possible without the brilliant David Vallejo’s HTML5 video listener code.

Here is the list of items you must complete in order to manually configure this:

  • HTML5 audio player listener is implemented via a Custom HTML tag
  • Custom Event Trigger was created that catches the audio events
  • Data Layer Variables that fetch the audioPlayerAction and audioTitle values from the Data Layer were created. Those values appear there because the listener does so
  • Finally, a Google Analytics 4 tag was created that fires whenever an audio event happens in the Data Layer. The tag also sends the values of audioPlayerAction and audioTitle.
Google Tag Manager Ebook Bundle
Julius Fedorovicius
In Google Tag Manager Recipes Google Tag Manager Tips
21 COMMENTS
Ralph Keser
  • Oct 14 2019
  • Reply

Hi Julius,

thanks for the great article. I was looking for a solution to track play and download. And the play event is implemented. But who can I track downloads?

Thanks and Greetings
Ralph

Ralph Keser
  • Oct 14 2019
  • Reply

Hi Julius,

thanks for the great article. I was looking for a solution to track play and download. And the play event is implemented. But how can I track downloads in the controller?

Thanks and Greetings
Ralph

    Julius Fedorovicius
    • Oct 14 2019
    • Reply

    I would try using the click trigger.

Arunprabu Sivapprakasam
  • Dec 19 2019
  • Reply

Hi Julius
Is it possible to fire a trigger when the user clicks the Download button inside the Audio player?
If yes please tell me how.

    Julius Fedorovicius
    • Dec 19 2019
    • Reply

    Use regular all element clicks trigger. If that does not work, you're out of luck.

Arunprabu Sivapprakasam
  • Dec 19 2019
  • Reply

It seems I am out of Luck. :(
I have tried it already.

Maxim
  • Feb 11 2020
  • Reply

Thank you very much! It works )

Renan
  • May 7 2020
  • Reply

Hi Julius, I did everything right, but there was no ready audio gift on the left side

    Julius
    • May 8 2020
    • Reply

    Maybe the player is not html5

Kushal Agarwal
  • Nov 5 2020
  • Reply

Hi Julius

What change do i make if i onyl want to track the plays and not the progress of pause events.

Also i have multiple pages where i have the audio element but only the audio elements on the landing page are firing a GA event and not on other pages.

Junghee
  • Jan 6 2021
  • Reply

Hi Julius,

I read the article well, thank you
I've tried changing the divisor to "5" but then it only fires at 5 and then at 50, 55, 60, etc.
Do you know what problem is it?

Thanks.
Junghee

Rebecca
  • Feb 8 2021
  • Reply

Fantastic tutorial - worked perfectly to capture play on an audio link (what I'm interested in tracking).

Brad
  • Oct 14 2022
  • Reply

This works for GA4 as well. Thanks, David and Julius.

Chris
  • Oct 18 2022
  • Reply

I am having a little trouble translating this to GA4. Would you consider updating this tutorial to the new standard?

Thank you

    Julius Fedorovicius
    • Oct 26 2022
    • Reply

    Probably, but not anytime soon.

    Everything remains the same except the tag. Just use the GA4 event tag. Event name can be whatever you want, like audio_{{dlv - audioPlayerAction}}, and then send a parameter "audio_file" of which value is another data layer variable.

    I would highly recommend investing more time into understanding how GTM and GA4 works. Your goal should be to understand the things under the hood (so that you would be able to build/troubleshoot similar things yourself). Otherwise, people just get stuck when a tutorial slightly does not match the current interface.

Verónica Santos
  • Aug 7 2023
  • Reply

Thank you very much for this tutorial, it is excellent and very well explained. I have a question to analyze the tracked data. How can I analyze the number of reproductions per user from GA4? and the average playback seconds per user? Is it done with the event "number of events by users" and filtering by "play"?

jay
  • Aug 10 2023
  • Reply

Hello, can it work with "window loaded" instead of "DOM ready"?

On dom ready event, the audio player isn't loaded yet, so the parameter is false, it became true only with winwow loaded for me.

    Julius Fedorovicius
    • Aug 10 2023
    • Reply

    Yes

      jay
      • Aug 10 2023
      • Reply

      i tried, but i don't get any audio event triggered in GTM preview.

Kati Nortrup
  • Mar 28 2024
  • Reply

Can I track how long or avg time spend on videos? What would the variable be?

Lochlan
  • Jun 20 2024
  • Reply

Unfortunately, this version and the GTM recipe are double-firing with each interaction. Is there a way to prevent this? There is no discernable difference in the variables of each of the firings when it occurs so I haven't been able to stop it.

Leave a comment Cancel reply

Your email address will not be published. Required fields are marked *


 

Hi, I'm Julius Fedorovicius and I'm here to help you learn Google Tag Manager and Google Analytics. Join thousands of other digital marketers and digital analysts in this exciting journey. Read more
Analytics Mania
  • Google Tag Manager Courses
  • Google Tag Manager Recipes
  • Google Tag Manager Resources
  • Google Tag Manager Community
  • Login to courses
Follow Analytics Mania
  • Subscribe to newsletter
Recent Posts
  • Conversion rate in Google Analytics 4
  • Google Tag Manager Data Layer Explained
  • Cross-domain tracking in Google Analytics 4
Analytics Mania - Google Tag Manager and Google Analytics Blog | Privacy Policy
Manage Cookie Settings