Optimizing the PHP MP3 waveform generator

Waveform generated using Dancing With Paris' "(Boardwalk)" MP3.

After some great comments in the original PHP MP3 Waveform Generator blog post, I decided to go ahead and do my best to optimize the script as much as possible. This included, as a commenter named Bruce pointed out, that it wasn’t very efficient on memory to perform a extraction of a large set of amplitude data points for the waveform only to discard them later when drawing on the canvas (given the DETAIL constant setting).

My revised script now estimates the number of amplitude points of the resulting decoded WAV file (data size) and extracts those points while at the same time plotting them onto the canvas. This eliminates the need for a second loop and reduces overall memory and performance.

UPDATE 2012-12-08: I’ve also added support for stereo (2-channel) waveform processing!

Based on a couple quick tests, the revised script reduced execution time by 20% and overall memory usage by 15%. Unfortunately the most time consuming portion of the script is still the lame resampling and decoding, but this really is a necessary evil: to take the MP3 and turn it into a usable, processable WAV file.

I also made a separate SVG version of the script which generates an SVG/XML file — this is definitely more usable for circumstances where you might not know the required dimensions of your image ahead of time. The resulting SVG can be resized any amount and will not lose quality.

In addition to optimizing the script, I’ve also added support (to both PNG and SVG versions) for generating 2-channel stereo waveforms. This can take a single MP3, split it into both left and right channels, and generate a single image with both waveforms.

These two projects have been uploaded (finally) to my github account:

php-waveform-png project on github

php-waveform-svg project on github

48 thoughts on “Optimizing the PHP MP3 waveform generator

  1. […] comments, I have optimized the performance of this script and made some brief commentary available here. I have also updated any source code links below to the new github project […]

  2. Your script is awesome, but at the moment, it only works on a DETAIL level of 1. When set to that value, the entire mp3 file is process and resultant waveform ends up in the image. However, for values > 1, the crux of the code does not operate correctly. It “skips” drawing the waveform data in the loop, but doesn’t skip over the waveform data properly. This can all be fixed by simply cutting line 152 of the code:

    if ($data_point++ % DETAIL == 0) {

    from its current position and pasting it right after this line:

    fseek($handle, $ratio, SEEK_CUR);

    Once this is done, the fseek operation skips over the data that isn’t to be drawn.

    Otherwise, LOVE your code and I’m using it to write my own SoundCloud-like player using SoundManager 2.

    // triune

    • Andrew

      Hi triune,

      Thank so much for the kind words!

      I was reviewing some of my code last night and experimenting around but I wasn’t able to replicate the problem you were having (moving the if statement before fseek actually rendered incorrectly compared to my latest github check-in). Are you able to paste your copy of the script to something like pastebin or provide a diff file so that I might be able to look at it more closely?

      Thanks again and all the best,


        • Andrew

          I experimented a bit with your revised script vs mine, but I seem to be getting incorrect waveform results with yours. I ran the two scripts and saved their output, both with the same settings (MP3 file, detail, width, height, etc) to compare results:

          Original (mine):

          Revised (yours):

          Comparing them side-by-side, the waveform being generated appears exactly the same, except that your script seems to be skipping too many data points too early and leaves the remainder of the waveform unprocessed. In order to fix that, you would have to modify the $data_size calculation (line 128 in your script) to match how much data is being processed/skipped.

          I created mine originally so that it only reads amplitude data and draws if it matches the DETAIL level, whereas yours seems to read data all the time, but only draw when it matches the DETAIL level (an unnecessary step?)

          I’m not sure, maybe I’m somehow getting magically different results on my laptop, but I’ll try to find another server setup and give it another go, but those are my initial results. Or, the more likely scenario is that I’m really just missing something so painfully obvious that’s resulting in my version of the script being completely wrong. Either way, I’ll experiment some more.

          • From the looks of your waveforms, it appears that you are testing on very small sound files. I was using DJ mixes that were 1-2 hours in length. Your original script only captured the first few minutes in the waveform leaving the majority of the waveform data truncated.

            I’m about to jet out the door, but I will come back here at some point with some image results as well as some other improvements I made to the script, such as smoothing and more accurately descriptive waveform images.

            // triune

  3. Arjhun

    I’m a big fan of you !! .. You’ve made some pretty awesome snippets of php code. Thank you so much, works perfectly, when my site goes live you’ll be included in the credits section with a redirect to this post and i’m keeping the license document with your php code itself. There’s no issues with this right cause this is the first time im handling anything with license ! 😀

    Have a good year ahead,
    Make many more snippets,


  4. Jurre

    Can you let this work on a Ubuntu server?

    • Andrew

      You shouldn’t have any problems getting this to work with Ubuntu — just make sure you have proper write permissions on the directory and that lame is installed.

  5. Nicholas Cooper

    Hey trying to get this to work on my shared hosting,

    I got a lot of errors popping up and I made sure that the directory had a CHMOD of 777 as well as the file itself. I’ve placed the lame.dll in the director yet no luck, I am missing a step or something please help thank you.

    • Andrew

      Based on what I can see, it looks like you’re running a Linux-based server, not Windows. In this case, the lame Windows executable files (DLL and EXE) are not going to be useful to you. With shared hosting it’s going to be really difficult, but you’ll probably need root access in order to install LAME on Linux or build from source.

      • Nicholas Cooper

        Thank you for your insight and expertise, that information is correct and I thought the same thing. I will be receiving a server within a month will I be able to then process this script?

        Thank You

  6. John


    I appreciate the effort you have made to create this script. It is exactly what I have been looking for.

    I have been trying to run it on my Windows machine for the last few days and have had no luck. I am hoping you have run into this problem before and know a solution.

    When I navigate to the .php and select my mp3 audio file, then hit Generate Waveform, the page will either refresh with a blank page. The waveform does not appear on the browser nor has it been created in the physical directory.

    I have placed the .php file, lame.exe, and lame_enc.dll in the same directory, they are the only files in the directory, and there are no other directories inside the root directory. The directory has write permissions. I have tried changing certain variables to see if it changes anything, it does not work. I am using various mp3s with different encodes, still no luck.

    Have you ever encountered this problem? Thanks.

    • Andrew

      Hi John,

      A couple of quick suggestions to try and determine the problem:

      First, if this is a development server (not public facing), make sure you have your PHP INI settings cranked up to show any errors.

      Secondly, the script is likely throwing some errors, you are just not able to see them because it is trying to display the error output as a PNG image (which obviously won’t work). Try commenting out the header(“Content-Type: image/png”); line near the bottom of the script and running it again. This should dump all of your errors properly to your browser window.

      Give those two things a shot and let me know what you get.


      • John

        Hi Andrew,

        I did as you suggested and removed the header. The error message I received is that the uploaded file doesn’t exist, so I uploaded the script to a server and tried it again to discover that it works. So apparently, the problem was on my end as the file is not being written to my folders. I gave my folders permissions, but for some reason it still doesn’t work.

        I’m gonna mess with it. I’m just keeping you updated so you know that the error was my fault.

        Thank you for the script, it is quite fantastic.

  7. Arjhun

    I just wanted to know why certain MP3 files don’t get analyzed and i get errors. Though i get the output waveform as an image for most, certain don’t process and an error pops up. Any thoughts on why this happens ?

    • Andrew

      Hi Arjhun,

      I really can’t provide much help on any errors you’re having unless you tell me what those errors they are.


  8. I had to come back here to make people aware of this free tool, its much better in many facets than the tool mentioned here, ie. logarithmic scale, rms, bordered with text information, etc, etc… still love the script here, its filled a gap I needed for a few months.


  9. If you are interested in converting an mp3 to a 8 kHz mono wav file in one go and without any intermediate files, you should check out Gstreamer (http://gstreamer.freedesktop.org/). This media framework can convert anything to everything. I have no experience with Gstreamer on Windows, but it should work. On my Debian laptop, I use the following command to convert a file:

    gst-launch-0.10 filesrc location=$IN ! decodebin ! audioconvert ! audioresample ! audio/x-raw-int,depth=16,channels=1,rate=8000 ! wavenc ! filesink location=$IN.wav

    For an average song, the conversion takes 5-8 seconds on a 4 year old Core2 Duo at 2.4GHz.

    Thanks for an otherwise great script; I only found it today after quite a few hours of Googling for some way to get this kind of data from an audio file.

  10. Hi – I am trying to use your mp3 > png converter but it does not work with my mp3s. I have tried a lot of mp3s. I did get it to convert a very short wav once and can see a blocky image, but usually, either the page simply reloads without anything appearing to happen, or I get the message ”The image “http://localhost/php-waveform-png.php” cannot be displayed because it contains errors. Could you offer any help – I’d love to use the waveforms from your converter rather than have to Photoshop the ones I have done.

    • Hi again – after some debugging I found it was the simple issue that my php.ini was set to limit file uploads to 2M which is the default but obviously way too small for this type of work! Might be obvious to others, but adding this here as there is no error handling/messaging in the script.

  11. Richard

    Any update on this? I noticed that the DETAIL code was not working.

    I’d also be interested to use a logarithmic scale to make the waveforms look more like those on soundcloud.

  12. Jack

    Hey trying to get this to work on my own server,

    but many errors… was wondering how to install Lame correctly?

    thank you 🙂

  13. Jack

    Hey, Ive been trying to find a way to implement this code to get a .svg output from an mp3 waveform. I am no coder and was wondering if anyone was hosting the code for me to upload my file to and download the vector file.
    Many thanks

  14. Declan

    Hey, I’ve managed to get this script to work on a local XAMPP installation and it works flawlessly for any .mp3 under 8MB in size. Any size bigger and it just doesn’t work – it’ll try and upload the song but then it’ll just stop. I’ve got a few songs that are much bigger than that (8MB – 21MB) and changing the values of upload_max_filesize, post_max_size and memory_limit in php.ini (the XAMPP default being 128M, 8M, 128M respectively) to 128M, 128M, 150M respectively did not seem to offer much difference. Is there anything I could do to get this to work? I’d really like to modify it and include it in a personal project of mine. Other than that great code!

  15. Kia

    Hey, love the script. I’ve been using a slightly modified version of this on my blog for a couple month of months now and it works flawlessly… Check it out here: http://www.vacayvitamins.com/. I have one question, is there anyway to generate a png image with a transparent background?

    • kia

      Nvm, figured it out

  16. is there an easy way to make this command line, like “draw my.file > out.svg” ?

    (my php knowhow is sketchy at best)

  17. Thanks for the great article and I really enjoyed reading your thought process as you came up with a solution for this task. I’m in the process of porting your code over to ColdFusion so I can come up with a similar solution. Unfortunately the JVM doesn’t seem to be as memory efficient with reading large wav files as PHP is.

    I like the idea of using SOX to handle the mp3 conversion by the way. If you haven’t tried it yet, I highly recommend looking it up. It seems a bit more flexible in terms of input/output formats… the only pain is to find the right compiled binary with the right libraries included.

    Thanks again and keep up the great work!

  18. Max K

    This looks like a great script but I can’t get php-waveform-png.php to work.

    I’m running CentOS 5 with LAME 32bits version 3.99.5 and PHP5.

    I set my max_upload to 100M and increased all related vars.

    I still get nothing when I upload. Just a blank screen no errors.

    I set detail to “1” still nothing.

    Any help is much appreciated.

  19. prashant

    Thanks…This is very g8 script…its useful to me…i am using this on wamp…i want to use it on linux server..so i think i need to install lame on linux..but after that is there need to update in script..Please guide me..Thanks in advance…

  20. Andrey

    Any chance to get a C# version of this code?

  21. Cool code , i have it working yesterday in my server but instead lame im using ffmpeg and it goes from mp3 to wav 8000khz in 5 seconds/ what can be done to improve the quality of the waveform itself, if you need advise on using with ffmpeg, send me a email and ill be glad to post the 1 line of code , that’s all you need.

    • Arjhun

      Hey Ricky,

      I’ve always wanted to speed up the time consumed when the conversion happens, since you’ve found a way to do that could you run me through the process ?

      Also, I want to compare 2 MP3 files, the idea that struck me was to store the graph points into a database and then cross check them with the other set of points. Wouldn’t that work ?

      • Stephen Fry

        Replace the exec() line with;

        exec(“ffmpeg -i {$tmpname}_o.mp3 -f wav -ar 8000 {$tmpname}.wav”);

        I tested it and he is correct, it saved seconds to convert it. Overall (including the waveform generation) it was still faster but only by a 0.4 secs. Using ffmpeg got me a 7mb wav and lame got me a 3mb wav. So if you decrease the “8000” you might be able to keep the quality higher but increase the speed more. It creates far more sexy waveforms with it set to 8000 though.

        • Stephen Fry

          Ah, ignore what I said about filesize above. Use this one instead to fix it:

          exec(“ffmpeg -i {$tmpname}_o.mp3 -f wav -ar 8000 -ac 1 {$tmpname}.wav”);

  22. Peter

    I would thank you for this wonderful script. I tried and used it. It works fine.
    I have minor issue like waves generated is very small and not more visual to view. Please let me know waves format more visual like http://andrewfreiday.com/temp/wav_original.png.

    Thanks in Advance.

    • Peter

      Any help on this. Even I tried to change values but it do not work accurate. May Be let me know what need to change for showing Wave form in better quality and more visual, as Width and Height are fixed ( 480 * 90 ).

  23. jc

    x-post from the other comment thread…

    I’m really surprised no one is offering just a simple MP3 -> waveform image application/web application with aesthetic customization options. Seriously — google around and you’ll find that no one offers such service while there are a lot of people asking for just that on Yahoo Answers and various forum threads.

    There are people just looking for a way to turn their MP3s/WAVs into waveform images! Surely someone out there can help the less technically inclined among us. Personally, I’ve been on a hunt for a program that will render high resolution spectrographs of WAVs, but I haven’t found anything.

    I absolutely love Nugen Audio’s spectrogram (http://www.nugenaudio.com/visualizer_video_tutorial.php), but there’s no way to save the visual output as an image. I even contacted the developers, but the visualizer was created under contract, and can’t be modified.

    Surely there is something out there…

  24. alireza

    hi. how can use in drupal 7 ?

  25. Stephen Fry

    Is it possible to use this for “audio fingerprint” generation? I want to find duplicate files (flac/mp3 etc will be converted to wav then ran through your script to get the waveform, how can I make it so it knows it’s the same track – the same songs but from different original encodings most likely have a tiny little difference in waveform/noise?)

    • Arjhun


      I did have a small idea on how to check if 2 MP3 files match. I’m not sure how accurate it would be.. But you could scroll up and check my response to Ricky. You’d see my idea there.

      I’ve just downloaded the source code and it seems to me at first glance that the $heading[] array holds the points for the graph, well it seems the most obvious.

      Andrew does a lot of calculations based on the values stored in $heading[], like finding the bitrate and whether the wave is mono or stereo, so I’m not sure where the points are stored.

      I’m going to try and work out a small script that matches just the points before I build on it.

      My exposure and knowledge in PHP is nothing notable, so if you’ve got experience in PHP I could use a hand, try and get back to me when you can 🙂


      • Stephen Fry

        Hi Arjhun,

        Yes, I have a good few (7) years of PHP knowledge. I actually managed to do it in the end (and it hasn’t produced any mistakes yet). I used the SVG source code (Assuming it uses the same variables as the PNG source code);

        Find “if ($y1 != $y2)” and inside there I made a string similar to $svg but only added them when it’s over a very low value (so you start from when the track actually starts), also I set DEFAULT_WIDTH rather small in size so the waveforms are pretty similar. I also increased “DETAIL” to a higher number; I sent an MD5 of the string I made to the database and so far it’s found the duplicates and hasn’t classed non-duplicates as duplicates.


  26. Vadim

    Hi, Andrew.
    I really appreciate your script.
    I myself can not install it on a website, can you help me install script for reward?
    Please contact me.

  27. Steffen

    thanks a lot for the skript! I’m totally (!) new to PHP and just searched a way to visualize my sounds.
    But whenever I upload a file, there appears either a Warning (Warning: POST Content-Length of 10997018 bytes exceeds the limit of 8388608 bytes in Unknown on line 0) or a little “broken path”-symbol.
    I would really appreciate a fast answer from anyone, i need that picture!

    Thank you and sorry for my english


  28. Nilesh

    This is very useful for me but it supports only .mp3 files , i wants to generate waveform with .mp3 and .wav files…
    Please help me how i can achieve it . . .

Leave a Reply

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

You may use these HTML tags and attributes:

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>