diff --git a/lib/tasks/tests/visual/run.js b/lib/tasks/tests/visual/run.js
index 44573a63589636ff389c27132404ce119838ff79..3c9a24ccb2557efd9848b96cbb59d6c0fc893398 100644
--- a/lib/tasks/tests/visual/run.js
+++ b/lib/tasks/tests/visual/run.js
@@ -27,15 +27,25 @@ import * as sauce from "~/lib/servers/sauce-connect"
 import * as selenium from "~/lib/servers/selenium"
 
 import Gemini from "gemini"
+import SauceLabs from "saucelabs"
+
+/* ----------------------------------------------------------------------------
+ * Locals
+ * ------------------------------------------------------------------------- */
+
+/* SauceLabs job name */
+const id = process.env.TRAVIS
+  ? `Travis #${process.env.TRAVIS_BUILD_NUMBER}`
+  : `Local #${moniker.choose()}`
+
+/* SauceLabs test results */
+const passed = {}
 
 /* ----------------------------------------------------------------------------
  * Task: run visual tests
  * ------------------------------------------------------------------------- */
 
 export default (gulp, config, args) => {
-  const id = process.env.TRAVIS
-    ? `Travis #${process.env.TRAVIS_BUILD_NUMBER}`
-    : `Local #${moniker.choose()}`
   return done => {
 
     /* Start static file server */
@@ -72,15 +82,15 @@ export default (gulp, config, args) => {
 
         /* Setup and run Gemini */
         .then(runner => {
-          const gemini = require(
+          const setup = require(
             path.join(process.cwd(), `${config.tests.visual}/config`,
               process.env.CI || process.env.SAUCE
                 ? "gemini.sauce-connect.json"
                 : "gemini.selenium.json"))
 
           /* Add dynamic configuration to capabilities */
-          for (const key of Object.keys(gemini.browsers)) {
-            const caps = gemini.browsers[key].desiredCapabilities
+          for (const key of Object.keys(setup.browsers)) {
+            const caps = setup.browsers[key].desiredCapabilities
             caps.tunnelIdentifier = id
             caps.public = "private"
             caps.name = id
@@ -90,8 +100,23 @@ export default (gulp, config, args) => {
               caps.public = "public"
           }
 
-          /* Start Gemini and return runner upon finish */
-          return new Gemini(gemini).test(`${config.tests.visual}/suites`, {
+          /* Setup Gemini and test listeners */
+          const gemini = new Gemini(setup)
+          if (process.env.CI || process.env.SAUCE) {
+
+            /* Initialize test run */
+            gemini.on(gemini.events.START_BROWSER, job => {
+              passed[job.sessionId] = true
+            })
+
+            /* Update state of test run */
+            gemini.on(gemini.events.TEST_RESULT, job => {
+              passed[job.sessionId] = passed[job.sessionId] && job.equal
+            })
+          }
+
+          /* Run tests */
+          return gemini.test(`${config.tests.visual}/suites`, {
             reporters: ["flat", "html"],
             browsers: args.browsers ? [].concat(args.browsers) : null
           })
@@ -110,6 +135,22 @@ export default (gulp, config, args) => {
           })
         })
 
+        /* Update SauceLabs jobs with test results */
+        .then(() => {
+          const saucelabs = new SauceLabs({
+            username: process.env.SAUCE_USERNAME,
+            password: process.env.SAUCE_ACCESS_KEY
+          })
+          const updates = Object.keys(passed).map(sessionId => {
+            return new Promise(resolve => {
+              saucelabs.updateJob(sessionId, {
+                passed: passed[sessionId]
+              }, resolve)
+            })
+          })
+          return Promise.all(updates)
+        })
+
     /* Stop static file server */
     })
       .then(() => {
diff --git a/package.json b/package.json
index a99c0a512d915f9391878d4c44d746f563442373..b1a9e64bb061c92c6200c71cbfc7824ded31cd5a 100644
--- a/package.json
+++ b/package.json
@@ -103,6 +103,7 @@
     "karma-webpack": "^2.0.1",
     "mocha": "^3.2.0",
     "moniker": "^0.1.2",
+    "saucelabs": "^1.4.0",
     "sauce-connect-launcher": "^1.2.0",
     "selenium-standalone": "^6.0.0"
   },
diff --git a/yarn.lock b/yarn.lock
index 5e98b86c2db612df413f695a6b6a32110211b70d..b3f1d37d64e5080d9cf61fed864e4591e60ac510 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3416,7 +3416,7 @@ https-browserify@0.0.1, https-browserify@~0.0.0:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
 
-https-proxy-agent@~1.0.0:
+https-proxy-agent@^1.0.0, https-proxy-agent@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-1.0.0.tgz#35f7da6c48ce4ddbfa264891ac593ee5ff8671e6"
   dependencies:
@@ -6206,6 +6206,12 @@ sauce-connect-launcher@^1.2.0:
     lodash "^4.16.6"
     rimraf "^2.5.4"
 
+saucelabs@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.4.0.tgz#b934a9af9da2874b3f40aae1fcde50a4466f5f38"
+  dependencies:
+    https-proxy-agent "^1.0.0"
+
 sax@~1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"