Download PDF

Web SDK.

The Web SDK allows third parties to run speed tests powered by SamKnows into their web applications. It doesn't provide an interface, just a library which you can use to run the test.

Supported tests

Due to the sandboxed nature of web browsers, the web test only runs a subset of the tests available in the router SDK or mobile apps:

  • Download speed
  • Upload speed
  • Latency
  • Jitter (derived from the latency test)

Supported browsers

We test the Web SDK in the following browsers:

  • Google Chrome on Windows
  • Firefox on Windows
  • Internet Explorer 11 on Windows
  • Microsoft Edge on Windows
  • Google Chrome on OS X
  • Safari on OS X
  • Firefox on OS X
  • Google Chrome on Android
  • Safari on iPhone

The latency and upload tests both use WebSockets in all browsers, and the download test uses the fetch streaming API in Chrome, Firefox, and Safari, and WebSockets in Internet Explorer and Edge—basically, it uses the fetch streaming API in all browsers that support it except for Edge, where it was unreliable.

Library API

The library is the most flexible way of using the web speed test, handling only the logic of running the test and leaving the interface completely up to the developer.

To initialise and start the test, create a new instance of the SpeedTest object imported from the library:

const st = new SpeedTest();

You can pass options into the constructor (covered below) but the defaults are reasonable.

The speed test will immediately start running. The returned object is an event emitter which can be used as follows:

st.on('progress', function (results) {
        // results is an object containing the test results
      });

The progress event is emitted whenever some data arrives from the server and the result of the currently in progress test is updated. This event can be emitted very often so can be a performance bottleneck if you try to animate the page on this event - it is more efficient to store the results to a variable and animate the page on requestAnimationFrame instead. If you use SamKnows's transform-when library passing the individual results in as custom variables, it handles this automatically.

st.on('progress-warmup', function (results) {
        // results is an object containing the test results
      });

The download and upload tests have warmup phases where they're downloading data, but the result isn't counted towards the final result. The progress-warmup event is emitted as if the test is running, but then the duration and bytes sent data is reset after the warmup phase and the test phase starts again from 0.

st.on('test-done', function (testName, results) {
        // testName is the name of the test that just finished
        // results is an object containing the test results
      });

The test-done event is emitted when a test finishes running (and thus, when the next one begins). It is useful for updating the user interface between tests.

Unlike most of the other events, this one has two arguments - the first is the name of the test that just finished, and the second is the object containing results as in the other events.

st.on('done', function (results) {
        // results is an object containing the test results
      });

The done event is emitted once when the test has finished running and the tests have been cleaned up (e.g. ensuring that all connections are now closed).

st.on('error', function (err) {
        // err is an Error object
      });

The error event is emitted when the test fails to complete. This can be for any number of reasons - check the error message for more info. Usually it's because of a network problem.

The results object

While the test is in progress, the results object will look like the following:

{
        "inProgress": "download",
        "latency": {
          "latency": 4.700000000048021,
          "jitter": 2.1000000002459274,
          "successes": 10,
          "failures": 0,
          "target": "10.0.0.106",
          "utc_datetime": "2018-06-12T10:27:30.599Z"
        },
        "download": {
          "bytes_sec": 30436705.76930629,
          "mbps": 243.49364615445032,
          "bytes_total": 204087040,
          "duration": 6284.699999999655,
          "successes": 1,
          "failures": 0,
          "target": "10.0.0.106",
          "utc_datetime": "2018-06-12T10:27:42.607Z"
        }
      }

When the test is complete, it will look something like this:

{
        "latency": {
          "latency": 3.540000000067812,
          "jitter": 0.7399999998611747,
          "successes": 10,
          "failures": 0,
          "target": "10.0.0.106",
          "utc_datetime": "2018-06-12T10:28:19.779Z"
        },
        "download": {
          "bytes_sec": 28178917.178344704,
          "mbps": 225.43133742675766,
          "bytes_total": 281783536,
          "duration": 9999.799999999595,
          "successes": 1,
          "failures": 0,
          "target": "10.0.0.106",
          "utc_datetime": "2018-06-12T10:28:31.778Z"
        },
        "upload": {
          "bytes_sec": 30436705.76930629,
          "mbps": 243.49364615445032,
          "bytes_total": 304087040,
          "duration": 9990.800000000483,
          "successes": 1,
          "failures": 0,
          "target": "10.0.0.106",
          "utc_datetime": "2018-06-12T10:28:43.825Z"
        }
      }

Please note:

  • Some properties, such as mbps and bytes_sec, are derived from other properties.
  • successes and failures will always be 0 or 1 on the download and upload tests.
  • utc_datetime is the time the test started; add the duration to get when the test finished.
  • The library does no rounding, leaving it completely up to the service consuming the data.
  • All durations are reported in microseconds.

Speed test options

You can pass options into the speed test by providing an object as the first argument to the SpeedTest constructor:

const st = new SpeedTest({
        global: {
          testServer: ['n1-the1.samknows.com', 'n2-the1.samknows.com', 'n3-the1.samknows.com'],
        },
        download: {
          duration: 7000,
        },
      });

The default options are as follows:

new SpeedTest({
        global: {
          testServer: '10.0.0.106', // This will not work for you
          wsTestServerPort: 6501,
          fetchTestServerPort: 80,
          wsConcurrency: 8,
          fetchConcurrency: 8,
          warmupDuration: 2000,
        },

        latency: {
          packets: 10,
        },

        download: {
          duration: 10000,

          startChunkSize: 16 * 1024,
          maxChunkSize: 1024 * 1024,
          maxChunkDuration: 1000,
        },

        upload: {
          duration: 10000,

          startChunkSize: 16 * 1024,
          maxChunkSize: 1024 * 1024,
          maxChunkDuration: 1000,
        },
      });

Each test looks at its own part of the object, then it looks at the global object if it isn't defined. For example, when the download test wants to know how long to run for, it looks at config.download.duration - it's defined, so it uses it. When it wants to know what server to test against, it looks at config.download.testServer, but that isn't defined so it uses config.global.testServer instead.

This means you can set configuration for all tests or configuration for individual tests depending on what your needs are.

You can override parts of the object or the whole object - the options you provide and the default options are merged using a deep merge strategy. Generally, most of the options can be left alone.

The only option you need to change is the testServer option, which can either be a string containing the host of the test server, or an array of servers - if an array, the speed test will run latency tests against all of them and use the quickest one.

Directory structure

The project only contains two files, this one and speed-test.js. speed-test.js is bundled using the UMD syntax, so you can import it using your package management system such as RequireJS, Browserify or Webpack, or you can add the file directly to your site and use the global SpeedTest variable.

Result ingestion

The speed test sends the results of the test back to the SamKnows ingestion server, configuration for which can be found in the ingestion object. You will probably never need to change the ingestionApi property, but you'll need to set the panelId to the ID of the panel you want the results reporting to.

The st object will emit a test-id event with the ID of the stored test once the ingestion API returns successful - this will happen after the done event.