diff --git a/packages/bemuse-notechart/src/index.ts b/packages/bemuse-notechart/src/index.ts index 548d9b094..f6c480b0e 100644 --- a/packages/bemuse-notechart/src/index.ts +++ b/packages/bemuse-notechart/src/index.ts @@ -204,6 +204,24 @@ export class Notechart { return this._spacing.factor(beat) } + /** + * Gets the keyMode from scratch + * @param scratch + * @returns {string} + */ + getKeyMode(scratch: string): string { + const usedColumns: { [column: string]: boolean } = {} + for (const note of this.notes) { + usedColumns[note.column] = true + } + if (scratch === 'off' && !usedColumns['1'] && !usedColumns['7']) return '5K' + if (scratch === 'left' && !usedColumns['6'] && !usedColumns['7']) + return '5K' + if (scratch === 'right' && !usedColumns['1'] && !usedColumns['2']) + return '5K' + return '7K' + } + _preTransform( bmsNotes: BMS.BMSNote[], playerOptions: Partial @@ -211,7 +229,7 @@ export class Notechart { let chain = _.chain(bmsNotes) let keys = getKeys(bmsNotes) if (playerOptions.scratch === 'off') { - chain = chain.map(note => { + chain = chain.map((note: BMS.BMSNote) => { if (note.column && note.column === 'SC') { return Object.assign({}, note, { column: null }) } else { diff --git a/packages/bemuse-tools/src/audio.js b/packages/bemuse-tools/src/audio.js index 6e175f39d..d25f08314 100644 --- a/packages/bemuse-tools/src/audio.js +++ b/packages/bemuse-tools/src/audio.js @@ -1,5 +1,4 @@ import Promise from 'bluebird' -import co from 'co' import fs from 'fs' import Throat from 'throat' import { cpus } from 'os' @@ -28,47 +27,35 @@ export class AudioConvertor { _doConvert(path, type) { return this._SoX(path, type) } - // TODO [#621]: Convert the `_SoX` method to async function (instead of using `co`) in [bemuse-tools] src/audio.js - // See issue #575 for more details. - _SoX(path, type) { - return co( - function*() { - let typeArgs = [] - try { - let fd = yield Promise.promisify(fs.open, fs)(path, 'r') - let buffer = Buffer.alloc(4) - let read = yield Promise.promisify(fs.read, fs)( - fd, - buffer, - 0, - 4, - null - ) - yield Promise.promisify(fs.close, fs)(fd) - if (read === 0) { - console.error('[WARN] Empty keysound file.') - } else if ( - buffer[0] === 0x49 && - buffer[1] === 0x44 && - buffer[2] === 0x33 - ) { - typeArgs = ['-t', 'mp3'] - } else if (buffer[0] === 0xff && buffer[1] === 0xfb) { - typeArgs = ['-t', 'mp3'] - } else if ( - buffer[0] === 0x4f && - buffer[1] === 0x67 && - buffer[2] === 0x67 && - buffer[3] === 0x53 - ) { - typeArgs = ['-t', 'ogg'] - } - } catch (e) { - console.error('[WARN] Unable to detect file type!') - } - return yield this._doSoX(path, type, typeArgs) - }.bind(this) - ) + async _SoX(path, type) { + let typeArgs = [] + try { + let fd = await Promise.promisify(fs.open)(path, 'r') + let buffer = Buffer.alloc(4) + let read = await Promise.promisify(fs.read)(fd, buffer, 0, 4, null) + await Promise.promisify(fs.close)(fd) + if (read === 0) { + console.error('[WARN] Empty keysound file.') + } else if ( + buffer[0] === 0x49 && + buffer[1] === 0x44 && + buffer[2] === 0x33 + ) { + typeArgs = ['-t', 'mp3'] + } else if (buffer[0] === 0xff && buffer[1] === 0xfb) { + typeArgs = ['-t', 'mp3'] + } else if ( + buffer[0] === 0x4f && + buffer[1] === 0x67 && + buffer[2] === 0x67 && + buffer[3] === 0x53 + ) { + typeArgs = ['-t', 'ogg'] + } + } catch (e) { + console.error('[WARN] Unable to detect file type!') + } + return this._doSoX(path, type, typeArgs) } _doSoX(path, type, inputTypeArgs) { return throat( diff --git a/src/auto-synchro/music/index.js b/src/auto-synchro/music/index.js index 4598a670b..45b7fff74 100644 --- a/src/auto-synchro/music/index.js +++ b/src/auto-synchro/music/index.js @@ -1,5 +1,4 @@ import _ from 'lodash' -import co from 'co' import context from 'bemuse/audio-context' import download from 'bemuse/utils/download' import SamplingMaster from 'bemuse/sampling-master' @@ -17,24 +16,21 @@ let ASSET_URLS = { /** * Loads the files and create a music instance. */ -export function load() { - // TODO [#626]: Convert the `load` function to async function (instead of using `co`) in src/auto-synchro/music.js - // See issue #575 for more details. - return co(function*() { - let master = new SamplingMaster(context) - let sample = name => - download(ASSET_URLS[`${name}.ogg`]) - .as('arraybuffer') - .then(buf => master.sample(buf)) - let samples = _.fromPairs( - yield Promise.all( - ['bgm', 'intro', 'kick', 'snare'].map(name => - sample(name).then(sampleObj => [name, sampleObj]) - ) +export async function load() { + let master = new SamplingMaster(context) + + let sample = name => + download(ASSET_URLS[`${name}.ogg`]) + .as('arraybuffer') + .then(buf => master.sample(buf)) + let samples = _.fromPairs( + await Promise.all( + ['bgm', 'intro', 'kick', 'snare'].map(name => + sample(name).then(sampleObj => [name, sampleObj]) ) ) - return music(master, samples) - }) + ) + return music(master, samples) } /** diff --git a/src/coming-soon/demo/index.js b/src/coming-soon/demo/index.js index 8fc76b834..bf38be09f 100644 --- a/src/coming-soon/demo/index.js +++ b/src/coming-soon/demo/index.js @@ -4,7 +4,6 @@ import $ from 'jquery' import { Compiler, Notes, Reader, SongInfo, Timing } from 'bms' import DndResources from 'bemuse/resources/dnd-resources' import SamplingMaster from 'bemuse/sampling-master' -import co from 'co' import ctx from 'bemuse/audio-context' import template from './template.jade' @@ -33,56 +32,53 @@ export function main(element) { } } -// TODO [#627]: Convert the `go` function to async function (instead of using `co`) in src/coming-soon/demo/index.js -// See issue #575 for more details. -function go(loader, element) { +async function go(loader, element) { let master = new SamplingMaster(ctx) let $log = element.find('.js-log') let $play = element.find('.js-play').hide() let $sampler = element.find('.js-sampler') - co(function*() { - log('Loading file list') - let list = yield loader.fileList - let bmsFile = list.filter(f => f.match(/\.(?:bms|bme|bml|pms)$/i))[0] - log('Loading ' + bmsFile) + log('Loading file list') + let list = await loader.fileList + let bmsFile = list.filter(f => f.match(/\.(?:bms|bme|bml|pms)$/i))[0] + log('Loading ' + bmsFile) - let arraybuffer = yield (yield loader.file(bmsFile)).read() - let buffer = Buffer.from(new Uint8Array(arraybuffer)) - let text = yield Promise.promisify(Reader.readAsync)(buffer) - let chart = Compiler.compile(text).chart - var timing = Timing.fromBMSChart(chart) - var notes = Notes.fromBMSChart(chart) - var info = SongInfo.fromBMSChart(chart) - $('
')
-      .text(JSON.stringify(info, null, 2))
-      .appendTo($sampler)
-    log('Loading samples')
-    var samples = yield loadSamples(notes, chart)
-    log('Click the button to play!')
-    yield waitForPlay()
-    void (function() {
-      master.unmute()
-      for (let note of notes.all()) {
-        setTimeout(() => {
-          let sample = samples[note.keysound]
-          if (!sample) {
-            console.log('warn: unknown sample ' + note.keysound)
-            return
-          }
-          let span = $('')
-            .text('[' + note.keysound + '] ')
-            .appendTo($sampler)
-          let instance = sample.play()
-          $sampler[0].scrollTop = $sampler[0].scrollHeight
-          instance.onstop = function() {
-            span.addClass('is-off')
-          }
-        }, timing.beatToSeconds(note.beat) * 1000)
-      }
-      return false
-    })()
-  }).done()
+  let loadedFile = await loader.file(bmsFile)
+  let arraybuffer = await loadedFile.read()
+  let buffer = Buffer.from(new Uint8Array(arraybuffer))
+  let text = await Promise.promisify(Reader.readAsync)(buffer)
+  let chart = Compiler.compile(text).chart
+  var timing = Timing.fromBMSChart(chart)
+  var notes = Notes.fromBMSChart(chart)
+  var info = SongInfo.fromBMSChart(chart)
+  $('
')
+    .text(JSON.stringify(info, null, 2))
+    .appendTo($sampler)
+  log('Loading samples')
+  var loadedSamples = await loadSamples(notes, chart)
+  log('Click the button to play!')
+  await waitForPlay()
+  void (function() {
+    master.unmute()
+    for (let note of notes.all()) {
+      setTimeout(() => {
+        let sample = loadedSamples[note.keysound]
+        if (!sample) {
+          console.log('warn: unknown sample ' + note.keysound)
+          return
+        }
+        let span = $('')
+          .text('[' + note.keysound + '] ')
+          .appendTo($sampler)
+        let instance = sample.play()
+        $sampler[0].scrollTop = $sampler[0].scrollHeight
+        instance.onstop = function() {
+          span.addClass('is-off')
+        }
+      }, timing.beatToSeconds(note.beat) * 1000)
+    }
+    return false
+  })()
 
   function waitForPlay() {
     return new Promise(function(resolve) {
@@ -98,17 +94,17 @@ function go(loader, element) {
     $log.text(t)
   }
 
-  function loadSamples(notes, chart) {
+  function loadSamples(_notes, _chart) {
     var samples = {}
     var promises = []
     let completed = 0
 
-    for (let note of notes.all()) {
+    for (let note of _notes.all()) {
       let keysound = note.keysound
       if (!(keysound in samples)) {
         samples[keysound] = null
         promises.push(
-          loadKeysound(chart.headers.get('wav' + keysound))
+          loadKeysound(_chart.headers.get('wav' + keysound))
             .then(blob => master.sample(blob))
             .then(sample => (samples[keysound] = sample))
             .catch(e => console.error('Unable to load ' + keysound + ': ' + e))
diff --git a/src/game/display/player-display.js b/src/game/display/player-display.js
index 3ba80aad9..d79741a20 100644
--- a/src/game/display/player-display.js
+++ b/src/game/display/player-display.js
@@ -5,15 +5,17 @@ import { getGauge } from './Gauge'
 
 export class PlayerDisplay {
   constructor(player, skinData) {
-    let notechart = player.notechart
     this._currentSpeed = 1
     this._player = player
-    this._noteArea = new NoteArea(notechart.notes, notechart.barLines)
+    this._noteArea = new NoteArea(
+      player.notechart.notes,
+      player.notechart.barLines
+    )
     this._stateful = {}
     this._defaultData = {
       placement: player.options.placement,
       scratch: player.options.scratch,
-      key_mode: getKeyMode(notechart, player.options.scratch),
+      key_mode: player.notechart.getKeyMode(player.options.scratch),
       lane_lift: Math.max(0, -player.options.laneCover),
       lane_press: Math.max(0, player.options.laneCover),
     }
@@ -221,16 +223,3 @@ export class PlayerDisplay {
 }
 
 export default PlayerDisplay
-
-// TODO [#629]: MOVE THIS (getKeyMode) TO bemuse-notechart
-//
-function getKeyMode(notechart, scratch) {
-  const usedColumns = {}
-  for (const note of notechart.notes) {
-    usedColumns[note.column] = true
-  }
-  if (scratch === 'off' && !usedColumns['1'] && !usedColumns['7']) return '5K'
-  if (scratch === 'left' && !usedColumns['6'] && !usedColumns['7']) return '5K'
-  if (scratch === 'right' && !usedColumns['1'] && !usedColumns['2']) return '5K'
-  return '7K'
-}
diff --git a/src/game/index.js b/src/game/index.js
index 7d0492a21..24c997006 100644
--- a/src/game/index.js
+++ b/src/game/index.js
@@ -3,7 +3,6 @@ import React from 'react'
 import SCENE_MANAGER from 'bemuse/scene-manager'
 import URLResource from 'bemuse/resources/url'
 import audioContext from 'bemuse/audio-context'
-import co from 'co'
 import query from 'bemuse/utils/query'
 import { resolveUrl } from 'url'
 import { unmuteAudio } from 'bemuse/sampling-master'
@@ -13,7 +12,7 @@ import GameScene from './game-scene'
 import GameShellScene from './ui/GameShellScene.jsx'
 import LoadingScene from './ui/LoadingScene.jsx'
 
-export function main() {
+export async function main() {
   // iOS
   window.addEventListener('touchstart', function unmute() {
     unmuteAudio(audioContext)
@@ -32,9 +31,7 @@ export function main() {
     })
   }
 
-  // TODO [#630]: Convert the `getSong` function to async function (instead of using `co.wrap`) in src/game/index.js
-  // See issue #575 for more details.
-  let getSong = co.wrap(function*() {
+  let getSong = async function() {
     let kbm = (query.keyboard || '').split(',').map(x => +x)
     let options = {
       url: query.bms || '/music/[snack]dddd/dddd_sph.bme',
@@ -63,7 +60,7 @@ export function main() {
         },
       ],
     }
-    options = yield displayShell(options)
+    options = await displayShell(options)
     let url = options.url
     let assetsUrl = resolveUrl(url, 'assets/')
     let metadata = {
@@ -73,27 +70,23 @@ export function main() {
       genre: '',
       subartists: [],
     }
-    let loadSpec = {
+    return {
       bms: options.resource || new URLResource(url),
       assets: options.resources || new BemusePackageResources(assetsUrl),
       metadata: metadata,
       options: Object.assign({}, options.game, { players: options.players }),
     }
-    return loadSpec
-  })
+  }
 
-  // TODO [#631]: Convert the `co` invocation to async function IIFE in src/game/index.js
-  co(function*() {
-    let loadSpec = yield getSong()
-    let { tasks, promise } = GameLoader.load(loadSpec)
-    yield SCENE_MANAGER.display(
-      React.createElement(LoadingScene, {
-        tasks: tasks,
-        song: loadSpec.metadata,
-      })
-    )
-    let controller = yield promise
-    yield SCENE_MANAGER.display(new GameScene(controller.display))
-    controller.start()
-  }).done()
+  let loadSpec = await getSong()
+  let { tasks, promise } = GameLoader.load(loadSpec)
+  await SCENE_MANAGER.display(
+    React.createElement(LoadingScene, {
+      tasks: tasks,
+      song: loadSpec.metadata,
+    })
+  )
+  let controller = await promise
+  await SCENE_MANAGER.display(new GameScene(controller.display))
+  controller.start()
 }
diff --git a/src/scene-manager/index.js b/src/scene-manager/index.js
index f1e055ff1..619353dbc 100644
--- a/src/scene-manager/index.js
+++ b/src/scene-manager/index.js
@@ -1,4 +1,3 @@
-import co from 'co'
 import React from 'react'
 import ReactDOM from 'react-dom'
 import MAIN from 'bemuse/utils/main-element'
@@ -60,41 +59,35 @@ export class SceneManager {
     })
   }
 
-  // TODO [#635]: Convert the `_transitionTo` method to async function (instead of using `co`) in src/scene-manager/index.js
-  // See issue #575 for more details.
-  _transitionTo(getNextScene) {
-    return co(
-      function*() {
-        if (this._transitioning) throw new Error('Scene is transitioning!')
-        try {
-          this._transitioning = true
+  async _transitionTo(getNextScene) {
+    if (this._transitioning) throw new Error('Scene is transitioning!')
+    try {
+      this._transitioning = true
 
-          // detach the previous scene
-          if (this.currentSceneInstance) {
-            yield Promise.resolve(this.currentSceneInstance.teardown())
-            detach(this.currentElement)
-          }
+      // detach the previous scene
+      if (this.currentSceneInstance) {
+        await Promise.resolve(this.currentSceneInstance.teardown())
+        detach(this.currentElement)
+      }
 
-          // obtain the next scene
-          let scene = getNextScene()
+      // obtain the next scene
+      let scene = getNextScene()
 
-          // coerce react elements
-          if (typeof scene !== 'function') {
-            scene = new ReactScene(scene, this.ReactSceneContainer)
-          }
+      // coerce react elements
+      if (typeof scene !== 'function') {
+        scene = new ReactScene(scene, this.ReactSceneContainer)
+      }
 
-          // set up the next scene
-          var element = document.createElement('div')
-          element.className = 'scene-manager--scene'
-          MAIN.appendChild(element)
-          this.currentElement = element
-          this.currentScene = scene
-          this.currentSceneInstance = scene(element)
-        } finally {
-          this._transitioning = false
-        }
-      }.bind(this)
-    )
+      // set up the next scene
+      var element = document.createElement('div')
+      element.className = 'scene-manager--scene'
+      MAIN.appendChild(element)
+      this.currentElement = element
+      this.currentScene = scene
+      this.currentSceneInstance = scene(element)
+    } finally {
+      this._transitioning = false
+    }
   }
 }
 
diff --git a/src/scintillator/index.spec.js b/src/scintillator/index.spec.js
index 80a0dc071..50a788d02 100644
--- a/src/scintillator/index.spec.js
+++ b/src/scintillator/index.spec.js
@@ -1,21 +1,14 @@
-import co from 'co'
-
 import * as Scintillator from './'
 
 let fixture = file => `/src/scintillator/test-fixtures/${file}`
 
 describe('Scintillator', function() {
   describe('#load', function() {
-    it(
-      'should load skin and return skin node',
-      // TODO [#636]: Convert all `co.wrap()` calls in src/scintillator/index.spec.js to async functions
-      // See issue #575 for more details.
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('bare.xml'))
-        expect(skin.width).to.equal(123)
-        expect(skin.height).to.equal(456)
-      })
-    )
+    it('should load skin and return skin node', async function() {
+      let skin = await Scintillator.load(fixture('bare.xml'))
+      expect(skin.width).to.equal(123)
+      expect(skin.height).to.equal(456)
+    })
     it('should reject if invalid', function() {
       return expect(Scintillator.load(fixture('invalid_tag.xml'))).to.be
         .rejected
@@ -23,29 +16,24 @@ describe('Scintillator', function() {
   })
 
   describe('Context', function() {
-    it(
-      'should instantiate and able to destroy',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('bare.xml'))
-        let context = new Scintillator.Context(skin)
-        context.render({})
-        context.destroy()
-      })
-    )
+    it('should instantiate and able to destroy', async function() {
+      let skin = await Scintillator.load(fixture('bare.xml'))
+      let context = new Scintillator.Context(skin)
+      context.render({})
+      context.destroy()
+    })
     describe('#input', function() {
       let skin
       let context
-      beforeEach(
-        co.wrap(function*() {
-          skin = yield Scintillator.load(fixture('bare.xml'))
-          context = new Scintillator.Context(skin)
-          context.render({})
-          context.view.style.position = 'fixed'
-          context.view.style.top = '0'
-          context.view.style.left = '0'
-          document.body.appendChild(context.view)
-        })
-      )
+      beforeEach(async function() {
+        skin = await Scintillator.load(fixture('bare.xml'))
+        context = new Scintillator.Context(skin)
+        context.render({})
+        context.view.style.position = 'fixed'
+        context.view.style.top = '0'
+        context.view.style.left = '0'
+        document.body.appendChild(context.view)
+      })
       afterEach(function() {
         context.destroy()
         document.body.removeChild(context.view)
@@ -88,81 +76,66 @@ describe('Scintillator', function() {
       })
     })
     describe('#refs', function() {
-      it(
-        'should be a set of refs to the display object',
-        co.wrap(function*() {
-          let skin = yield Scintillator.load(fixture('refs.xml'))
-          let context = new Scintillator.Context(skin)
-          context.render({})
-          expect(Array.from(context.refs['a'])[0]).to.equal(
-            context.stage.children[0]
-          )
-          context.destroy()
-        })
-      )
-    })
-  })
-
-  describe('Expressions', function() {
-    it(
-      'should be parsed and processed',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_basic.xml'))
+      it('should be a set of refs to the display object', async function() {
+        let skin = await Scintillator.load(fixture('refs.xml'))
         let context = new Scintillator.Context(skin)
         context.render({})
-        let stage = context.stage
-        expect(stage.children[0].x).to.equal(6)
-        expect(stage.children[0].y).to.equal(7)
-        context.destroy()
-      })
-    )
-    it(
-      'should support variables',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_variables.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ a: 4, b: 3 })
-        expect(stage.children[0].x).to.equal(7)
-        expect(stage.children[0].y).to.equal(12)
-        context.render({ a: 10, b: 20 })
-        expect(stage.children[0].x).to.equal(30)
-        expect(stage.children[0].y).to.equal(200)
+        expect(Array.from(context.refs['a'])[0]).to.equal(
+          context.stage.children[0]
+        )
         context.destroy()
       })
-    )
+    })
+  })
+
+  describe('Expressions', function() {
+    it('should be parsed and processed', async function() {
+      let skin = await Scintillator.load(fixture('expr_basic.xml'))
+      let context = new Scintillator.Context(skin)
+      context.render({})
+      let stage = context.stage
+      expect(stage.children[0].x).to.equal(6)
+      expect(stage.children[0].y).to.equal(7)
+      context.destroy()
+    })
+    it('should support variables', async function() {
+      let skin = await Scintillator.load(fixture('expr_variables.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({ a: 4, b: 3 })
+      expect(stage.children[0].x).to.equal(7)
+      expect(stage.children[0].y).to.equal(12)
+      context.render({ a: 10, b: 20 })
+      expect(stage.children[0].x).to.equal(30)
+      expect(stage.children[0].y).to.equal(200)
+      context.destroy()
+    })
   })
 
   describe('SpriteNode', function() {
-    it(
-      'should allow setting sprite frame',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('sprite_attrs.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({})
-        let frame = stage.children[0].texture.frame
-        expect(frame.width).to.equal(10)
-        expect(frame.height).to.equal(11)
-        expect(frame.x).to.equal(12)
-        expect(frame.y).to.equal(13)
-        context.destroy()
-      })
-    )
-    it(
-      'should allow setting visibility, width, height',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('sprite_attrs.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({})
-        let sprite = stage.children[0]
-        expect(sprite.width).to.equal(3)
-        expect(sprite.height).to.equal(1)
-        expect(sprite.visible).to.equal(false)
-        context.destroy()
-      })
-    )
+    it('should allow setting sprite frame', async function() {
+      let skin = await Scintillator.load(fixture('sprite_attrs.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({})
+      let frame = stage.children[0].texture.frame
+      expect(frame.width).to.equal(10)
+      expect(frame.height).to.equal(11)
+      expect(frame.x).to.equal(12)
+      expect(frame.y).to.equal(13)
+      context.destroy()
+    })
+    it('should allow setting visibility, width, height', async function() {
+      let skin = await Scintillator.load(fixture('sprite_attrs.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({})
+      let sprite = stage.children[0]
+      expect(sprite.width).to.equal(3)
+      expect(sprite.height).to.equal(1)
+      expect(sprite.visible).to.equal(false)
+      context.destroy()
+    })
     it('should reject if blend mode is invalid', function() {
       return expect(Scintillator.load(fixture('sprite_invalid_blend.xml'))).to
         .be.rejected
@@ -170,197 +143,161 @@ describe('Scintillator', function() {
   })
 
   describe('TextNode', function() {
-    it(
-      'should display text',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('text.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({})
-        let text = stage.children[0].children[0]
-        expect(text.text).to.equal('Hello world')
-        context.destroy()
-      })
-    )
-    it(
-      'should center text',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('text_center.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({})
-        let text = stage.children[0].children[0]
-        expect(text.x).to.be.lessThan(0)
-        context.destroy()
-      })
-    )
-    it(
-      'should support data interpolation',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('text_interpolation.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ lol: 'wow' })
-        let text = stage.children[0].children[0]
-        expect(text.text).to.equal('Hello world wow')
-        context.destroy()
-      })
-    )
+    it('should display text', async function() {
+      let skin = await Scintillator.load(fixture('text.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({})
+      let text = stage.children[0].children[0]
+      expect(text.text).to.equal('Hello world')
+      context.destroy()
+    })
+    it('should center text', async function() {
+      let skin = await Scintillator.load(fixture('text_center.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({})
+      let text = stage.children[0].children[0]
+      expect(text.x).to.be.lessThan(0)
+      context.destroy()
+    })
+    it('should support data interpolation', async function() {
+      let skin = await Scintillator.load(fixture('text_interpolation.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({ lol: 'wow' })
+      let text = stage.children[0].children[0]
+      expect(text.text).to.equal('Hello world wow')
+      context.destroy()
+    })
   })
 
   describe('IfNode', function() {
-    it(
-      'should display child when correct value',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_if.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ a: 'b' })
-        expect(stage.children[0].children).to.have.length(1)
-        context.destroy()
-      })
-    )
-    it(
-      'should not display child when correct value',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_if.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ a: 'x' })
-        expect(stage.children[0].children).to.have.length(0)
-        context.destroy()
-      })
-    )
+    let skin
+    let context
+    let stage
+    beforeEach(async function() {
+      skin = await Scintillator.load(fixture('expr_if.xml'))
+      context = new Scintillator.Context(skin)
+      stage = context.stage
+    })
+    afterEach(function() {
+      context.destroy()
+    })
+    it('should display child when correct value', function() {
+      context.render({ a: 'b' })
+      expect(stage.children[0].children).to.have.length(1)
+    })
+    it('should not display child when correct value', function() {
+      context.render({ a: 'x' })
+      expect(stage.children[0].children).to.have.length(0)
+    })
   })
 
   describe('ObjectNode', function() {
-    it(
-      'should display children',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_object.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ notes: [] })
-        expect(stage.children[0].children).to.have.length(0)
-        context.render({ notes: [{ key: 'a', y: 20 }] })
-        expect(stage.children[0].children).to.have.length(1)
-        context.render({ notes: [{ key: 'a', y: 20 }, { key: 'b', y: 10 }] })
-        expect(stage.children[0].children).to.have.length(2)
-        context.render({ notes: [{ key: 'b', y: 10 }] })
-        expect(stage.children[0].children).to.have.length(1)
-        context.destroy()
-      })
-    )
-    it(
-      'should update same array with content changed',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_object.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        let notes = []
-        context.render({ notes })
-        expect(stage.children[0].children).to.have.length(0)
-        notes.push({ key: 'a', y: 20 })
-        context.render({ notes })
-        expect(stage.children[0].children).to.have.length(1)
-        context.destroy()
-      })
-    )
-    it(
-      'should let children get value from item',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('expr_object_var.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        context.render({ notes: [] })
-        context.render({ notes: [{ key: 'a', y: 20 }] })
-        expect(stage.children[0].children[0].y).to.equal(20)
-        context.render({ notes: [{ key: 'a', y: 20 }, { key: 'b', y: 10 }] })
-        expect(stage.children[0].children[0].y).to.equal(20)
-        context.render({ notes: [{ key: 'b', y: 10 }] })
-        expect(stage.children[0].children[0].y).to.equal(10)
-        context.destroy()
-      })
-    )
+    it('should display children', async function() {
+      let skin = await Scintillator.load(fixture('expr_object.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({ notes: [] })
+      expect(stage.children[0].children).to.have.length(0)
+      context.render({ notes: [{ key: 'a', y: 20 }] })
+      expect(stage.children[0].children).to.have.length(1)
+      context.render({ notes: [{ key: 'a', y: 20 }, { key: 'b', y: 10 }] })
+      expect(stage.children[0].children).to.have.length(2)
+      context.render({ notes: [{ key: 'b', y: 10 }] })
+      expect(stage.children[0].children).to.have.length(1)
+      context.destroy()
+    })
+    it('should update same array with content changed', async function() {
+      let skin = await Scintillator.load(fixture('expr_object.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      let notes = []
+      context.render({ notes })
+      expect(stage.children[0].children).to.have.length(0)
+      notes.push({ key: 'a', y: 20 })
+      context.render({ notes })
+      expect(stage.children[0].children).to.have.length(1)
+      context.destroy()
+    })
+    it('should let children get value from item', async function() {
+      let skin = await Scintillator.load(fixture('expr_object_var.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      context.render({ notes: [] })
+      context.render({ notes: [{ key: 'a', y: 20 }] })
+      expect(stage.children[0].children[0].y).to.equal(20)
+      context.render({ notes: [{ key: 'a', y: 20 }, { key: 'b', y: 10 }] })
+      expect(stage.children[0].children[0].y).to.equal(20)
+      context.render({ notes: [{ key: 'b', y: 10 }] })
+      expect(stage.children[0].children[0].y).to.equal(10)
+      context.destroy()
+    })
   })
 
   describe('GroupNode', function() {
-    it(
-      'should allow masking',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('group_mask.xml'))
-        let context = new Scintillator.Context(skin)
-        let stage = context.stage
-        let mask = stage.children[0].mask
-        expect(mask).not.to.equal(null)
-        context.destroy()
-      })
-    )
+    it('should allow masking', async function() {
+      let skin = await Scintillator.load(fixture('group_mask.xml'))
+      let context = new Scintillator.Context(skin)
+      let stage = context.stage
+      let mask = stage.children[0].mask
+      expect(mask).not.to.equal(null)
+      context.destroy()
+    })
   })
 
   describe('AnimationNode', function() {
-    it(
-      'should allow animations',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('animation.xml'))
-        let context = new Scintillator.Context(skin)
-        let group = context.stage.children[0]
-        context.render({ t: 0 })
-        expect(group.x).to.equal(10)
-        expect(group.y).to.equal(0)
-        context.render({ t: 0.5 })
-        expect(group.x).to.equal(15)
-        expect(group.y).to.equal(1)
-        context.render({ t: 1 })
-        expect(group.x).to.equal(20)
-        expect(group.y).to.equal(2)
-        context.destroy()
-      })
-    )
-    it(
-      'should allow animations on different events',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('animation.xml'))
-        let context = new Scintillator.Context(skin)
-        let group = context.stage.children[0]
-        context.render({ t: 0.5, exitEvent: 0.5 })
-        expect(group.x).to.equal(50)
-        expect(group.y).to.equal(0)
-        context.render({ t: 1, exitEvent: 0.5 })
-        expect(group.x).to.equal(60)
-        expect(group.y).to.equal(50)
-        context.render({ t: 1.5, exitEvent: 0.5 })
-        expect(group.x).to.equal(70)
-        expect(group.y).to.equal(100)
-        context.destroy()
-      })
-    )
-    it(
-      'should allow animations on different value',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('animation_timekey.xml'))
-        let context = new Scintillator.Context(skin)
-        let group = context.stage.children[0]
-        context.render({ t: 0, x: 0.5 })
-        expect(group.x).to.equal(15)
-      })
-    )
+    it('should allow animations', async function() {
+      let skin = await Scintillator.load(fixture('animation.xml'))
+      let context = new Scintillator.Context(skin)
+      let group = context.stage.children[0]
+      context.render({ t: 0 })
+      expect(group.x).to.equal(10)
+      expect(group.y).to.equal(0)
+      context.render({ t: 0.5 })
+      expect(group.x).to.equal(15)
+      expect(group.y).to.equal(1)
+      context.render({ t: 1 })
+      expect(group.x).to.equal(20)
+      expect(group.y).to.equal(2)
+      context.destroy()
+    })
+    it('should allow animations on different events', async function() {
+      let skin = await Scintillator.load(fixture('animation.xml'))
+      let context = new Scintillator.Context(skin)
+      let group = context.stage.children[0]
+      context.render({ t: 0.5, exitEvent: 0.5 })
+      expect(group.x).to.equal(50)
+      expect(group.y).to.equal(0)
+      context.render({ t: 1, exitEvent: 0.5 })
+      expect(group.x).to.equal(60)
+      expect(group.y).to.equal(50)
+      context.render({ t: 1.5, exitEvent: 0.5 })
+      expect(group.x).to.equal(70)
+      expect(group.y).to.equal(100)
+      context.destroy()
+    })
+    it('should allow animations on different value', async function() {
+      let skin = await Scintillator.load(fixture('animation_timekey.xml'))
+      let context = new Scintillator.Context(skin)
+      let group = context.stage.children[0]
+      context.render({ t: 0, x: 0.5 })
+      expect(group.x).to.equal(15)
+    })
   })
 
   describe('defs', function() {
-    it(
-      'should allow reuse of skin nodes',
-      co.wrap(function*() {
-        let skin = yield Scintillator.load(fixture('defs.xml'))
-        let context = new Scintillator.Context(skin)
-        context.render({})
-        let stage = context.stage
-        expect(stage.children[0].x).to.equal(6)
-        expect(stage.children[0].y).to.equal(7)
-        expect(stage.children[1].x).to.equal(6)
-        expect(stage.children[1].y).to.equal(7)
-        context.destroy()
-      })
-    )
+    it('should allow reuse of skin nodes', async function() {
+      let skin = await Scintillator.load(fixture('defs.xml'))
+      let context = new Scintillator.Context(skin)
+      context.render({})
+      let stage = context.stage
+      expect(stage.children[0].x).to.equal(6)
+      expect(stage.children[0].y).to.equal(7)
+      expect(stage.children[1].x).to.equal(6)
+      expect(stage.children[1].y).to.equal(7)
+      context.destroy()
+    })
   })
 })
diff --git a/src/scintillator/loader.js b/src/scintillator/loader.js
index 4112b4f62..6e17ed85e 100644
--- a/src/scintillator/loader.js
+++ b/src/scintillator/loader.js
@@ -1,6 +1,5 @@
 import * as PIXI from 'pixi.js'
 import $ from 'jquery'
-import co from 'co'
 import debug from 'debug'
 import url from 'url'
 import { PERCENTAGE_FORMATTER } from 'bemuse/progress/formatters'
@@ -10,36 +9,30 @@ import Resources from './resources'
 
 const log = debug('scintillator:loader')
 
-// TODO [#637]: Convert the `load` function to async function (instead of using `co`) in src/scintillator/loader.js
-// See issue #575 for more details.
-export function load(xmlPath, progress) {
-  return co(function*() {
-    log('load XML from %s', xmlPath)
-    let $xml = yield loadXml(xmlPath)
+export async function load(xmlPath, progress) {
+  log('load XML from %s', xmlPath)
+  let $xml = await loadXml(xmlPath)
 
-    // scan all images
-    let resources = new Resources()
-    let paths = new Set()
-    for (let element of Array.from($xml.find('[image]'))) {
-      paths.add($(element).attr('image'))
-    }
-    for (let element of Array.from($xml.find('[font-src]'))) {
-      paths.add($(element).attr('font-src'))
-    }
-    for (let path of paths) {
-      let assetUrl = url.resolve(xmlPath, path)
-      resources.add(path, assetUrl)
-    }
+  // scan all images
+  let resources = new Resources()
+  let paths = new Set()
+  for (let element of Array.from($xml.find('[image]'))) {
+    paths.add($(element).attr('image'))
+  }
+  for (let element of Array.from($xml.find('[font-src]'))) {
+    paths.add($(element).attr('font-src'))
+  }
+  for (let path of paths) {
+    let assetUrl = url.resolve(xmlPath, path)
+    resources.add(path, assetUrl)
+  }
 
-    // load all images + progress reporting
-    yield loadResources(resources, progress)
+  // load all images + progress reporting
+  await loadResources(resources, progress)
 
-    // compile the skin
-    log('compiling')
-    let skin = new Compiler({ resources }).compile($xml)
-
-    return skin
-  })
+  // compile the skin
+  log('compiling')
+  return new Compiler({ resources }).compile($xml)
 }
 
 function loadXml(xmlUrl) {
diff --git a/tasks/pre-deploy.js b/tasks/pre-deploy.js
index 55ff8df0e..5b5b14db8 100644
--- a/tasks/pre-deploy.js
+++ b/tasks/pre-deploy.js
@@ -1,22 +1,16 @@
 import gulp from 'gulp'
 import fs from 'fs'
 import path from '../config/path'
-import co from 'co'
 import Promise from 'bluebird'
 
 const readFile = Promise.promisify(fs.readFile, fs)
 
-gulp.task(
-  'pre-deploy',
-  // TODO [#638]: Convert the `co.wrap()` call in tasks/pre-deploy.js to async function
-  // See issue #575 for more details.
-  co.wrap(function*() {
-    let data = yield readFile(path('dist', 'index.html'), 'utf-8')
-    check('New Relic inlined', () => /NREUM/.test(data))
-    check('Boot script inlined', () => /webpackJsonp/.test(data))
-    check('Google Analytics inlined', () => /GoogleAnalyticsObject/.test(data))
-  })
-)
+gulp.task('pre-deploy', async function() {
+  const data = await readFile(path('dist', 'index.html'), 'utf-8')
+  check('New Relic inlined', () => /NREUM/.test(data))
+  check('Boot script inlined', () => /webpackJsonp/.test(data))
+  check('Google Analytics inlined', () => /GoogleAnalyticsObject/.test(data))
+})
 
 function check(title, condition) {
   if (condition()) {