text: <!DOCTYPE html>
  <style>
      body {
          background-color: #eee;
          text-align: center;
          padding-top: 20px;
      }
      
      #emblem {
          width: 128px;
          height: 128px;
          border: 1px solid black;
      }
      
      #scratch {
          width: 128px;
          height: 128px;
          border: 1px solid red;
          display: none;
      }
  </style>
  <canvas id="emblem"></canvas>
  <canvas id="scratch"></canvas>
  <script>
  window.onload = function() {
      var baseUrl  = "https://api.guildwars2.com",
          guildId = "4BBB52AA-D768-4FC6-8EDE-C299F2822F0F",
          steps;
      
      function getJSON(url, cb) {
          var xhr = new XMLHttpRequest();
          
          xhr.onreadystatechange = function() {
              if(xhr.readyState !== 4) {
                  return;
              }
              
              return cb(JSON.parse(xhr.responseText));
          };
          
          xhr.open("GET", baseUrl + url, true);
          xhr.send();
      }
      
      return getJSON("/v1/guild_details.json?guild_id=" + guildId, function(guild) {
          var data = {
                  guild      : guild,
                  colors     : null,
                  background : null,
                  foreground : null
              },
              colorIds = [
                  guild.emblem.background_color_id,
                  guild.emblem.foreground_primary_color_id,
                  guild.emblem.foreground_secondary_color_id
              ];
          
          function checkPromise() {
              var missing = Object.keys(data).some(function(x) {
                      return !data[x];
                  });
              
              if(!missing) {
                  loadImages(data);
              }
          }
          
          // XXX: You really want to use promises here, but w/e this is super-vanilla.js.
          getJSON("/v2/colors?ids=" + colorIds.join(","), function(colors) {
              data.colors = colors.reduce(
                  function(a, x) {
      	            // XXX: THESE ARE NOT THE RIGHT COLOR VALUES.
      	            // For how to obtain the color objects to the RGB
      	            // values used for colorization, refer to:
      	            //  * https://gist.github.com/codemasher/1cf2a5b0c323b0e67aef
      	            //  * https://forum-en.guildwars2.com/forum/community/api/How-To-Colors-API
      	            //  * https://forum-en.guildwars2.com/forum/community/api/API-Suggestion-Guilds
  
                      a[x.id] = {
                          r : x.cloth.rgb[0],
                          g : x.cloth.rgb[1],
                          b : x.cloth.rgb[2]
                      };
                      return a;
                  },
                  {}
              );
              checkPromise();
          });
          
          getJSON("/v2/emblem/backgrounds/" + guild.emblem.background_id, function(bg) {
              data.background = bg;
              checkPromise();
          });
          
          getJSON("/v2/emblem/foregrounds/" + guild.emblem.foreground_id, function(fg) {
              data.foreground = fg;
              checkPromise();
          });
      });
      
      function loadImages(data) {
          var allLayers = data.background.layers.concat(data.foreground.layers),
              loaded    = 0;
          
          data.images = {};
          
          // XXX: Promises would be nice here too.
          return allLayers.forEach(function(x) {
              var img = new Image();
              
              img.addEventListener("load", function() {
                  data.images[x] = img;
                  
                  if(Object.keys(data.images).length === allLayers.length) {
                      renderEmblem(data);
                  }
              });
              
              img.crossOrigin = "anonymous";
              img.src = x;
          });
      }
      
      function colorshift(ctx, img, rgb) {
          console.log(arguments);
          var data, bits, len;
  
          ctx.clearRect(0, 0, 128, 128);
          
          ctx.drawImage(
              img,
              0, 0, 128, 128,
              0, 0, 128, 128
          );
          
          data = ctx.getImageData(0, 0, 128, 128);
          bits = data.data;
          len = bits.length;
          
          for(var p = 0; p < len; p += 4) {
              bits[p + 0] = rgb.r;
              bits[p + 1] = rgb.g;
              bits[p + 2] = rgb.b;
              // Don't mess with the alpha.
          }
          
          ctx.putImageData(data, 0, 0);
      }
      
      function renderEmblem(data) {
          console.log(data);
          
          var $emblem    = document.querySelector("#emblem"),
              $scratch   = document.querySelector("#scratch"),
              ctxEmblem  = $emblem.getContext("2d"),
              ctxScratch = $scratch.getContext("2d"),
              background = data.background,
              foreground = data.foreground,
              emblem     = data.guild.emblem;
          
          $emblem.height = 128;
          $emblem.width = 128;
          $scratch.height = 128;
          $scratch.width = 128;
          
          function drawImage(image) {
              ctxEmblem.drawImage(
                  typeof image === "string"
                      ? data.images[image]
                      : image,
                  0, 0, 128, 128,
                  0, 0, 128, 128
              );
          }
          
          function drawColored(image, color) {
              colorshift(
                  ctxScratch,
                  data.images[image],
                  typeof color === "object"
                      ? color
                      : data.colors[color]
              );
              
              drawImage($scratch);
          }
          
          ctxEmblem.save();
              if(emblem.flags.indexOf("FlipBackgroundHorizontal") !== -1) {
                  ctxEmblem.scale(-1, 1);
                  ctxEmblem.translate(-128, 0);
              }
          
              if(emblem.flags.indexOf("FlipBackgroundVertical") !== -1) {
                  ctxEmblem.scale(1, -1);
                  ctxEmblem.translate(0, -128);
              }
          
              drawColored(background.layers[0], emblem.background_color_id);
          ctxEmblem.restore();
          
          ctxEmblem.save();
              if(emblem.flags.indexOf("FlipForegroundHorizontal") !== -1) {
                  ctxEmblem.scale(-1, 1);
                  ctxEmblem.translate(-128, 0);
              }
          
              if(emblem.flags.indexOf("FlipForegroundVertical") !== -1) {
                  ctxEmblem.scale(1, -1);
                  ctxEmblem.translate(0, -128);
              }
          
              // XXX: Not sure if this one is correct.
              drawColored(foreground.layers[0], {r:0, g:0, b:0});
  
              drawColored(foreground.layers[1], emblem.foreground_primary_color_id);
              drawColored(foreground.layers[2], emblem.foreground_secondary_color_id);
          ctxEmblem.restore();
      }
  };
  </script>
  