Record Audio in Browser using RecorderJS and upload to Django Server Backend

If you want to record Audio samples in your browser without using Flash you probably already found RecorderJS or something similar. However no example showed how to transmit the recorded data to a server. Usually all examples create a blob that the user can download.
I will show you a very simple example based on Chris Rudmins RecorderJS fork. He added an Ogg Opus encoder that will you record directly in a compressed format. This is quite important as the regular RecorderJS implementation will record audio data as WAV which results in large amounts of data. E.g. 10 Mbyte / Minute which the user would have to upload – not viable if the user has not enough bandwidth. There is also a liblame JS implementation which will convert the recorded WAV into an MP3 in the users browser before uploading it, however this conversion takes quite long even on i7 CPUs. On my MBPr it encoded in realtime and then you still have to upload the results. That’s why recording to Ogg Opus directly is so awesome – the filesize will be similar to MP3.

So anyway, fire up the example.html provided by RecorderJS and add this Javascript function:

// Required for Django CSRF
function getCookie(name) {
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
        return cookieValue;

// Actual Upload function using xhr
function upload(blob, progressBar){
        var csrftoken = getCookie('csrftoken');

        var xhr = new XMLHttpRequest();'POST', 'upload/', true);
        xhr.setRequestHeader("X-CSRFToken", csrftoken);
        xhr.setRequestHeader("MyCustomHeader", "Put anything you need in here, like an ID");

        xhr.upload.onloadend = function() {
            alert('Upload complete');
        // If you want you can show the upload progress using a progress bar
        //var progressBar = document.querySelector('progress');
        xhr.upload.onprogress = function(e) {
            if (e.lengthComputable) {
                progressBar.value = (e.loaded / * 100;
                progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.


Basically we make use of XMLHttpRequest to do a POST request to a upload/ URL. We can add custom headers too (e.g. ID of the recording or something else you need in your backend) and we need the CSRF token, otherwise Django will block the POST request. If you like you can use the onloadend or onprogress callbacks as well so display the current status to the user. This is not necessary though.

Now to tie everything together, I’ve extended the recorder.addEventListener(“dataAvailable”) function. I’m not an expert with Javascript to there are hundreds of different solutions. I chose to add an upload button to each recording in this example.

recorder.addEventListener( "dataAvailable", function(e){
            var fileName = new Date().toISOString() + "." + e.detail.type.split("/")[1];
            var url = URL.createObjectURL( e.detail );

            var audio = document.createElement('audio');
            audio.controls = true;
            audio.src = url;

            var link = document.createElement('a');
            link.href = url;
   = fileName;
            link.innerHTML =;

// New Code starts here
            var progress = document.createElement('progress');
            progress.min = 0;
            progress.max = 100;
            progress.value = 0;
            var progressText = document.createTextNode("Progress: ");

            var button = document.createElement('button');
            var t = document.createTextNode("Upload?");
   = 'button';
            button.onclick = function() {
                upload(e.detail, progress);  

// All that's left is to add the button and Progress bar to the list            
            var li = document.createElement('li');            


In Django create a new view (don’t forget to add it to which handles the upload. You should add some more boilerplate code, like checking if the request is request.POST, etc.

The important part is that you can get your custom headers (you can also print request.META for debugging purposes to see what is transfered) and the actual audio data is hidden in request.body

def upload(request):

    customHeader = request.META['HTTP_MYCUSTOMHEADER']

    # obviously handle correct naming of the file and place it somewhere like media/uploads/
    uploadedFile = open("recording.ogg", "wb")
    # the actual file is in request.body
    # put additional logic like creating a model instance or something like this here
    return HttpResponse(escape(repr(request)))

That’s it. Hopefully it was helpful for somebody. Last thing to note is that recording currently only works in FF and Chrome. IE does not support recording, neither does Safari. Haven’t tested Opera (is this still around?). Edge can record, but can not play back Ogg Opus. All browsers tested in October 2015. On Mobile Devices you can not record using getUserMedia (it might work on Android), instead you have to use HTML Media Capture and then the upload will be handled by a HTML form. I might write a 2nd tutorial about this in the future.

12 thoughts on “Record Audio in Browser using RecorderJS and upload to Django Server Backend

  1. Hi,
    I’m trying to do the same task that you explain in this example. But, I never get the wav file in my server. The process ends perfectly with the alert(‘Upload complete’);

    The only difference between your code and mine is that you implement the button and progress in recorder.addEventListener( “dataAvailable”, function(e){… and I implement the similar code in function createDownloadLink(). Moreover, I need a wav file instead of ogg.

    I ckeck that blob has a similar value as blob:http…. in the source tag and the blob is sent by xhr.send(blob).

    Do you explain all steps in detail in your example? It’s very important for me and this part took me 5 days without any result.

    Thanks in advance and I like so much your webpage.



    • You can also use the original RecorderJS from Matt Diamond: if you want to use WAV instead of ogg.

      The XHR Request and the View in Django have been reduced for this example. If you want a copy of the full source code, let me know.

      If there are problems with the upload it is most likely that the process has no permission to write in the specific folder. I had some troubles when moving from local deployment to an Apache Webserver with WSGI. It really helps to turn on logging in Django! Then you will see the error message and can also see what is going on in the backend.

      To enable logging, just paste this into your and update the location of the logfile to your liking.

      LOGGING = {
      'version': 1,
      'disable_existing_loggers': False,
      'formatters': {
      'verbose': {
      'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
      'datefmt': "%d/%b/%Y %H:%M:%S"
      'simple': {
      'format': '%(levelname)s %(message)s'
      'handlers': {
      'file': {
      'level': 'INFO',
      'class': 'logging.FileHandler',
      'filename': '/var/log/myDjango.log',
      'formatter': 'verbose'
      'loggers': {
      'django': {
      'handlers': ['file'],
      'propagate': True,
      'level': 'INFO',
      'recorder': {
      'handlers': ['file'],
      'level': 'INFO',

      In your add the following:

      import logging
      logger = logging.getLogger(__name__)

      And in your upload(request) you can then add additional log statements

      def upload(request):
      if request.method == 'POST':"Received Upload POST request: %s" % request.META)
      baseDir = settings.BASE_DIR
      # TODO handle errors with multiple try / except blocks - right now it's like a catch all
      if request.user.is_anonymous():
      path = baseDir+"/media/recordings/AnonymousUser/"
      path = baseDir+"/media/recordings/" + request.user.username + "/"
      userID =
      filename = str(uuid.uuid1()) + "-" +"%Y-%m-%d_%H-%M-%S")"Filepath: %s" % path)
      if not os.path.exists(path):"Creating new Folder: %s" % path)
      os.makedirs(path)"Writing file: %s" % filename)
      uploadedFile = open(path+filename + ".ogg", "wb")

      Edit: Ah for some reason, the code format gets totally messed up in the comments. So do not copy & paste the python code, the logger section should still work though. Or just google on how to configure logging for Django. Also helps with local deployment when using runserver

      • Tobias,

        Can you send me your full source code?

        I tried to follow your done, I failed.

        If you send me your code, I am very happy.

          • Hi Harsh,

            unfortunately the project is not open-source (yet). Nothing stops you from checking out the rendered HTML and Javascript over at (you do not need to sign up to access the recorder). But after I saw your comment and since I just recently updated the recording library to also support video and not just audio – I decided to make a new blog post showing how to record and upload videos to Django. I hope this is helpful for you.


          • Hi Tobias

            Two things-
            1.) Where is the example.html file that you are talking about in above post?

            2.) Can you just send me a link where matt diamond’s recorderjs project has been customised such that the file is saved in django server. I am asking because I want the file in .wav format

            Your help is very appreciated. Thank you

          • 1. If you follow the link to the github Repository, it will include an example folder
            2. That is what I did here wit this blog post. Basically from all these recorders you will get a blob file which is stored in your browsers’ memory and which you can playback or “download” and store on your PC. What I did was send this blob file using a form (or xhr request + form) to the server. This is basically the same as an upload file form. The original RecorderJS supports only wav but nowadays you have more WebRTC based Javascript libraries available. Checkout the RecordJS I used in the other blog post. You can certainly configure if you want to use .wav or some other format (depends on the browser too). Also finally Safari works as well (thanks Apple).

            I have yet to implement the recorder on mobile devices but a quick test on Android worked fine. Not sure about iPhone.

          • I understand. But I want to ask you, is there a direct way of sending it to my server rather than downloading on the client system and then him uploading on server?

          • When you say “downloading on the client system and then uploading to the server” you are right and wrong. Again, what I show here is how to record using your browser to your local memory. You can “download” (save) the file to your PC (basically copy it from memory to HDD) and then manually upload the file again. What I implemented here is more like an automatic upload of the blob file that is stored in memory. The user can play it back and then decide to upload it or not.
            If what you are a looking for, is a real-time recording directly on the server than you need some kind of media server running on your server (e.g. Kurento) which you connect to and then record the stream directly on the server. The advantage is that the recording is immediately stored on the server, however we wanted our users to be able to check and play back or discard the recording before deciding if they are going to upload it.

            Hooking the blob directly to the upload form is a convenient way for the user to only press one button to upload the file to the server, instead of having it to save to disk and then manually uploading it.

Leave a Reply

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