       0, 0,      # src x, y
       dim..., dim... # No size change
     return dst
+
+  # Build a JPEG version of the image and invoke the callback
+  # with the data. The resulting data is also stored in
+  # @info.image_jpeg
+  #
+  # Requires `@info.image` to exist
+  #
+  # cb invoked as `cb(error, data)`
+  buildJPEG: (cb=(->)) =>
+    return cb(new Error("@info.image does not exist")) unless @info.image
+
+    # Save locally, cleanup
+    name = "ayp-#{@info.when}.jpg"
+    outPath = path.resolve("/tmp", name)
+    @info.image.saveJpeg outPath, 95, (err) ->
+      return cb(new Error("Failed to write result: #{err}")) if err
+      fs.readFile outPath, (err, data) ->
+        return cb(new Error("Failed to read file I just wrote to #{outPath}: #{err}")) if err
+        # We can unlink unconditionally now that we have it or failed
+        fs.unlink(outPath, ->)
+        cb(false, data)
+
 
 # PantsBuffer is the abstraction of "The Logs".
 #

        # Save locally, upload, cleanup
        now = strip.info.when
        name = "ayp-#{now}.jpg"
        outPath = path.resolve("/tmp", name)
        image.saveJpeg outPath, 95, (err) ->
          return console.error "Failed to write result:", err if err

          fs.readFile outPath, (err, data) ->
            # We can unlink unconditionally now that we have it or failed
            fs.unlink(outPath, ->)
            return msg.reply "I somehow lost the file I just put down at #{outpath}. Like a moron :(" if err
            return msg.reply "You hve no S3 creds bub" unless [AYP_AWS_KEY, AYP_AWS_SECRET, AYP_AWS_BUCKET].every (p) -> p?.length

            info =

    else
      msg

  # Composite `sprite` onto `dst` in full.
  # Offsets `sprite` `+left` from the left
  # and `+top` from the top
  compositeImage: (dst, sprite, left, top) ->
