SoundCloud for Developers

Discover, connect and build

We use cookies for various purposes including analytics and personalized marketing. By continuing to use the service, you agree to our use of cookies as described in the Cookie Policy.

Backstage Blog RSS

You're browsing posts of the category JavaScript

  • January 24th, 2019 Engineering JavaScript Redux React Garbage Collection in Redux Applications By Jan Monschke

    This post describes why and how we implemented a garbage collector in our Xbox application on top of Redux and in addition to the JavaScript engine’s regular garbage collector.

    Read more...

  • October 1st, 2015 Announcements API JavaScript SDKs Introducing SoundCloud JavaScript SDK 3.0.0 By Jan Monschke

    We are happy to announce version 3.0.0 of our SoundCloud JavaScript SDK.

    The new SDK improves stream security and content uploading functionality, and modernizes the technology stack.

    Version 3 of the SoundCloud JavaScript SDK is a major update and is not backwards compatible. That said, the changes that you need to make your web app work with the new SDK are easy to implement. Please refer to Migrating to JavaScript SDK 3.0.0 to upgrade.

    ECMAScript 2015 and CommonJS

    The original version of the SDK was written in CoffeeScript, which is no longer a core technology at SoundCloud. This update provided us the opportunity to migrate our source code from CoffeeScript to ECMAScript 2015.

    The new SDK is now using the Babel compiler for ES2015 support, and webpack as our bundler. This has the additional benefit of making the SoundCloud JavaScript SDK 3.0.0 compliant with CommonJS.

    Because of this, we can now take advantage of the variety of packages that are available via npm, and users can install the SDK via npm as well. Please refer to the npm page for details.

    JavaScript Promises

    Promises have become a core part of JavaScript with the new ES2015 specification, and they allow for better composability and a easier control flow. Internally, we work with Promises a lot and wanted to give external developers the ability to benefit from an easier API.

    Web Audio API

    The previous SDK neither provided a way to record sounds from Web Audio applications nor a way to upload Web Audio recordings. It shipped with a Flash component that handled recording and uploading of recordings. There was no way to specify an external file for uploading.

    The new SDK ships with a recorder component that uses Web Audio and getUserMedia instead of Flash, and the component allows you to pass in arbitrary AudioNodes that will be recorded. This makes it much easier to integrate the SoundCloud JavaScript SDK 3.0.0 into creator applications that rely on Web Audio. The SDK provides a dedicated method to publish recordings directly from your web app.

    Secure streaming

    The new SDK now includes a new player component. This component improves security for creator content and provides the improved playback stability.

    Documentation

    We also took the time to all of rewrite our documentation and code examples so that you can start with the new SDK immediately.

  • May 1st, 2014 Announcements JavaScript SDKs Introducing JavaScript SDK version 2 By Erik Michaels-Ober

    SoundCloud is pleased to introduce a new major version of the SoundCloud JavaScript SDK. In version 2, we've rewritten much of the internal code, resulting in better performance for your JavaScript applications and support for more streaming standards, such as HTTP Live Streaming.

    You can test the new version by pointing your JavaScript applications to http://connect.soundcloud.com/sdk-2.0.0.js.

    We've also created a guide to help you upgrade from version 1 to version 2.

    JavaScript SDK version 1 is now deprecated and will be permanently replaced by version 2 on July 1, 2014.

    On June 17, 2014, we will temporarily replace version 1 with version 2 between 10:00 and 11:00 UTC. We will do this again on June 24, 2014, between 18:00 and 19:00 UTC. These two upgrade tests will give you an opportunity to understand the impact of this change on your applications. To ensure a seamless transition for your users, we strongly encourage you to upgrade and perform internal tests in advance of these dates.

    To receive notices before, during, and after these tests, follow @SoundCloudDev on Twitter.

    If you have any questions about this upgrade, please feel free to email api-support@soundcloud.com.

  • February 20th, 2014 JavaScript Smooth image loading by upscaling By Nick Fisher

    The site soundcloud.com is a single-page application that displays a multitude of users’ images. At SoundCloud, we use a technique to make the loading of an image appear smooth and fast. When displaying an image on screen, we want it to display to the user as fast as possible. The images display in multiple locations from a tiny avatar on a waveform to a large profile image. For this reason, we create each image in several sizes. If you are using Gravatar, this technique also applies because you can fetch arbitrarily-sized images by passing the desired size in a query parameter (?s=).

    avatar-tiny avatar-small avatar-medium avatar-large

    The technique uses the browser’s cache of previously loaded images. When displaying a large avatar image, first display a smaller version that is stretched out to full size. When the larger image has loaded, it fades in over the top of the smaller version.

    The HTML looks like this:

    <img class="placeholder" src="small.jpg" width="200" height="200">
    <img class="fullImage" src="large.jpg" width="200" height="200">
    

    The CSS looks like this:

    .fullImage {
      transition: opacity 0.2s linear;
    }
    

    For the sake of brevity, the positioning code is not included in the preceding snippet. However, the images should lie atop one another.

    Finally, the JavaScript code looks like this:

    var fullImage   = $('.fullImage'),
        placeholder = $('.placeholder');
    
    fullImage
      .css('opacity', 0)
      .on('load', function () {
        this.style.opacity = 1;
        setTimeout(placeholder.remove.bind(placeholder), 500);
      });
    

    Thus far, it’s not too complicated, and it gives a nice effect to the loading of images.

    But there’s a problem: we don’t want to make a request to get the small image just to display it for a few milliseconds. The overhead of making HTTP requests means that loading the larger image will usually not take significantly longer than the small one. Therefore, it only makes sense to use this technique if a smaller image has already been loaded during a particular session and thus served from the browser’s cache. How do we know what images are in cache? Each time an avatar is loaded, we need to keep track of that. However over time, there could be many thousands of avatars loaded within one session, so it needs to be memory efficient. Instead of tracking the full URLs of loaded images, we extract the minimum amount of information to identify a image, and use a bitmask to store what sizes have been loaded:

    // a simple map object, { identifier => loaded sizes }
    var loadedImages = {},
    
      // Let's assume a basic url structure like this:
      // "http://somesite.com/{identifier}-{size}.jpg"
      imageRegex = /\/(\w+)-(\w+)\.jpg$/,
    
      // a list of the available sizes.
      // format is [pixel size, filename representation]
      sizes = [
        [ 20, "tiny"  ],
        [ 40, "small" ],
        [100, "medium"],
        [200, "large" ]
      ];
    
    // extract the identifier and size.
    function storeInfo(url) {
      var parts = imageRegex.exec(url),
          id    = parts[1]
          size  = parts[2],
          index;
    
      // find the index which contains this size
      sizes.some(function (info, index) {
        if (info[1] === size) {
          loadedImages[id] |= 1 << index;
          return true;
        }
      });
    }
    
    // once the image has loaded, then store it into the map
    $('.fullImage').load(function () {
      storeInfo(this.src);
    });
    

    When the image loads, we extract the important parts from the URL: namely, the identifier and the size modifier. Each size is mapped to a number—its index in the sizes array—and the appropriate bit is turned on in the loadedImages map. The code on line 27 does this conversion and bit manipulation; 1 << index is essentially the same as Math.pow(2, index). By storing only a single number in the object, we save quite a bit of memory. A single-number object could contain many different flags. For example, assume there are four different sizes and 10,000 images in the following map:

    asBools = {
      a: [true, true, false, true],
      b: [false, true, false, false],
      // etc...
    };
    
    asInts = {
      a: 11,  // 2^0 + 2^1 + 2^3 = 1 + 2 + 8
      b: 2,   // 2^1
      // etc...
    }
    

    The memory footprints of these objects differ by 30%: 1,372,432 bytes for the booleans, and 1,052,384 for the integers. The key names consume the largest portion of these objects’ memory. For this reason, it is important to compress the key names as much as possible. Numeric keys are stored particularly efficiently by the V8 JavaScript engine found in Chrome and Safari.

    We now have a map that shows us what images have been loaded during this session, and you can use that information to choose a placeholder:

    // find the largest image smaller than the requested one
    function getPlaceholder(fullUrl) {
      var parts = imageRegex.exec(fullUrl),
          id = parts[1],
          targetSize = parts[2],
          targetIndex;
    
      sizes.some(function (info, index) {
        if (info[1] < targetSize) {
          targetIndex = index;
          return true;
        }
      });
    
      while (targetIndex >= 0) {
        if (loadedImages[id] & 1 << targetIndex) {
          return fullUrl.replace(
            /\w+\.jpg$/,
            sizes[targetIndex][1] + '.jpg'
          );
        }
        --targetIndex;
      }
    }
    
    // and in usage:
    var placeholderUrl = getPlaceholder(fullSizeUrl);
    
    if (placeholderUrl) {
      // there has been a smaller image loaded previously, so...
      addTheFadeInBehaviour();
    } else {
      // no smaller image has been loaded so...
      loadFullSizeAsNormal();
    }
    

    Although, this technique is a bit involved and I’ve deliberately glossed over some of the details, it creates a nice visual effect, greatly reduces the perceived load time of the image, and it is especially effective for long-lived, single-page applications.

  • September 9th, 2013 JavaScript Writing your own Karma adapter By Misha Reyzlin

    Background

    When we started to work on the new version of our mobile web app, we knew we wanted to run unit tests on a wide variety of clients, mobile devices, PhantomJS, and on Chrome when running locally. Because we practice continuous integration, we knew we also wanted Git hooks and proper results formatting.

    We chose Karma runner, which is a project from the Angular JS team that provides developers with a “productive testing environment”. One of the advantages that Karma runner offers over other similar projects is its ability to use any testing framework. At SoundCloud, we aim to have the same toolset across various JavaScript projects, and our unit test framework of choice is Tyrtle.

    Using Tyrtle

    You can write your own Karma adapter by using the Tyrtle example that follows. The idea is to tie your tests to the Karma API. The pieces of information that you need are the number of tests, test suites or modules, the results of each test (with possible assertion or execution errors, or both), and a hook to let Karma know that the runner ran all of the tests.

    You also need to provide a start function that configures the unit test framework, loads the test files, and starts the tests.

    The basic template for an adapter is as follows:

    (function (win) {
    /**
     * Returned function is used to kick off tests
     */
    function createStartFn(karma) {
      return function () {
      };
    }
    
    /**
     * Returned function is used for logging by Karma
     */
    function createDumpFn(karma, serialize) {
      // inside you could use a custom `serialize` function
      // to modify or attach messages or hook into logging
      return function () {
        karma.info({ dump: [].slice.call(arguments) });
      };
    }
    
    win.__karma__.start = createStartFn(window.__karma__);
    win.dump = createDumpFn(win.__karma__, function (value) {
      return value;
    });
    })(window);
    

    Next, create a renderer/reporter for the unit test framework that will pass the data to Karma. Tyrtle has a renderer that can render HTML, XML for CI, or print to any other type of output.

    To pass the data to Karma, implement the methods that follow:

    /**
     * Tyrtle renderer
     * @interface
     */
    function Renderer () {}
    Renderer.prototype.beforeRun  = function (tyrtle) {};
    Renderer.prototype.afterRun   = function (tyrtle) {};
    Renderer.prototype.afterTest  = function (test, module) {};
    

    The createStartFn function creates a renderer object, with a Karma runner instance available within the start-function’s scope.

    Create a parameter named karma:

    function TyrtleKarmaRenderer (karma) {
      this.karma = karma;
    }
    

    Tell karma what the total number of tests is:

    /**
     * Invoked before all tests are run; reports complete number of tests
     * @param  {Object} tyrtle Instance of Tyrtle unit tests runner
     */
    TyrtleKarmaRenderer.prototype.beforeRun = function (tyrtle) {
      this.karma.info({
        // count number of tests in each of the modules
        total: tyrtle.modules.reduce(function(memo, currentModule) {
          return memo + currentModule.tests.length;
        }, 0)
      });
    };
    

    After each test, pass the resulting data to Karma:

    /**
     * Invoked after each test, used to provide Karma with feedback
     * for each of the tests
     * @param  {Object} test current test object
     * @param  {Object} module instance of Tyrtle module
     *                  to which this test belongs
     */
    TyrtleKarmaRenderer.prototype.afterTest = function (test, module) {
      this.karma.result({
        description: test.name,
        suite: [module.name + "#"] || [],
        success: test.status === Tyrtle.PASS,
        log: [test.statusMessage] || [],
        time: test.runTime
      });
    };
    

    Next, inform Karma that the tests have all finished running:

    /**
     * Invoked after all the tests are finished running
     * with unit tests runner as a first parameter.
     * `window.__coverage__` is provided by Karma.
     * This function notifies Karma that the unit tests runner is done.
     */
    TyrtleKarmaRenderer.prototype.afterRun = function (/* tyrtle */) {
      this.karma.complete({
        coverage: window.__coverage__
      });
    };
    

    You now have a renderer constructor. Next, turn your attention to the createStartFn function. You need to configure and initialize the unit test framework that returns a function, which requires a list of test files that are served from the Karma server and starts the actual runner.

    Karma serves the files that are required for testing from a path that Karma creates and timestamps the files to avoid caching issues in browsers. Karma makes each path available as a key in a hash named __karma__.files. This makes Karma a bit tricky to configure when using an AMD-loader such as require.js. To understand how to use AMD with Karma, go to:

    http://karma-runner.github.io/0.8/plus/RequireJS.html

    Here is the final createStartFn function:

    /**
     * Creates instance of Tyrtle to run the tests.
     *
     * Returned start function is invoked by Karma runner when Karma is
     * ready (connected with a browser and loaded all the required files)
     *
     * When invoked, the start function will AMD require the list of test
     * files (saved by Karma in window.__karma__.files) and set them
     * as test modules for Tyrtle and then invoke Tyrtle runner to kick
     * off tests.
     *
     * @param  {Object} karma Karma runner instance
     * @return {Function}     start function
     */
    function createStartFn(karma) {
      var runner = new Tyrtle({});
    
      Tyrtle.setRenderer(new TyrtleKarmaRenderer(karma));
      return function () {
        var testFiles = Object.keys(window.__karma__.files)
          .filter(function (file) {
            return (/-test\.js$/).test(file);
          })
          .map(function (testFile) {
            return testFile.replace('/base/public/', '').replace('.js', '');
          });
        require(testFiles, function (testModules) {
          // test files can return a single module, or an array of them.
          testFiles.forEach(function (testFile) {
            var testModule = require(testFile);
            if (!Array.isArray(testModule)) {
              testModule = [testModule];
            }
            testModule.forEach(function (aModule, index) {
              aModule.setAMDName(testFile, index);
              runner.module(aModule);
            });
          });
          runner.run();
        });
      };
    }
    

    To find more examples of how this all fits together, see the scripts test-main.js (the RequireJS configuration to work with Karma) and karma.conf.js. Also, there are many adapter implementations such as Mocha, NodeUnit, and QUnit on the Karma GitHub page.

    Ursula Kallio contributed to the writing of this post.