           (t) ->
             obj.material.uniforms.opacity.value = i(t)
 
-  fadeToArray = (to, duration) ->
-    (array) ->
-      new Promise (resolve) ->
+  fadeToArray: (to, duration) ->
+    (array) =>
+      new Promise (resolve) =>
         selection = d3.selectAll array
-        addFadeOpacityTransition(to, duration)(selection)
+        @addFadeOpacityTransition(to, duration)(selection)
           .each "end", resolve
-
-  fadeToArray: fadeToArray
 
   fadeAll: (objects, to, duration) ->
       promises = objects.map (child) =>
           return @fadeToArray(to, duration) child.children
       return Promise.all promises
+
+  chainedFadeIn: (array, duration) ->
+    reduction = (promise, curr, index, array) =>
+      promise.then =>
+        @fadeToArray(1, 1000) curr._text_object.children
+
+    promise = array.reduce reduction, Promise.resolve()
 
   _radianScale = d3.scale.linear()
       .domain([0, 360])


  wait: (duration) =>
    return new Promise (resolve) =>
      setTimeout(resolve, duration)

  zoomCameraToPosition: (target, duration) =>
    new Promise (resolve) =>
      d3.transition()
        .duration duration
        .tween "zoomCamera", =>
          current = @camera.position
          z = d3.interpolate current.z, target.z

    return bbox.box
  getBBox: getBBox

  ###########################################################################
  ###########################################################################
  # ACTIONS

  panCameraToPosition3: (target, duration, ignoreGlobal) =>
    z_offset = if ignoreGlobal? then 0 else CAMERA_Z
    dur = (duration || 1000) * @speedMultiplier
    new Promise (resolve) =>
      d3.transition()
        .duration dur
        .tween "moveCamera", =>
          current = @camera.position
          x = d3.interpolate(current.x, target.x)
