diff --git a/package.json b/package.json
index 63296bff9d3db05f684c3305fb106b30bf770dcc..9ff615051caf0a14600e83c5fc8faaa8dce4769a 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,7 @@
     "easyrtc": "^1.1.0",
     "express": "^4.15.5",
     "networked-aframe": "https://github.com/netpro2k/networked-aframe#dev",
+    "pleasejs": "^0.4.2",
     "socket.io": "^2.0.3"
   },
   "devDependencies": {
diff --git a/public/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png b/public/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png
index d0f89bc1497e8bc97c7c035abde9705e1316b613..3f7585fbd2ec32b717296daedb765cad19b9daa7 100644
Binary files a/public/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png and b/public/assets/avatars/dodec/DodecAvatarGLTF/AvatarDodeca_Texture.png differ
diff --git a/public/index.html b/public/index.html
index 3125bcf20e2c8b88ba8cb019d4f982f16c6da954..956d5c4c2b1a76e02aafddaf1a6d93088fd5e5fc 100644
--- a/public/index.html
+++ b/public/index.html
@@ -26,7 +26,14 @@
 
       <!-- Templates -->
       <script id="head-template" type="text/html">
-        <a-entity class="head" gltf-model="#dodec-avatar-head" position="0 0 0" networked-audio-source audio-feedback></a-entity>
+        <a-entity
+            class="head"
+            gltf-model="#dodec-avatar-head"
+            position="0 0 0"
+            networked-audio-source
+            audio-feedback
+            avatar-customization
+        ></a-entity>
       </script>
 
       <script id="hand-template" type="text/html">
diff --git a/src/components/avatar-customization.js b/src/components/avatar-customization.js
new file mode 100644
index 0000000000000000000000000000000000000000..8346b8e821ac904d8711dffe6563643f7628ba09
--- /dev/null
+++ b/src/components/avatar-customization.js
@@ -0,0 +1,49 @@
+import Please from "pleasejs";
+
+// @TODO this whole thing is a bit of a hack. This should probably just be setting some uniforms on a sharder.
+// @TODO the palate should be indexed by alpha channel instead of the red channel.
+// @TODO color should be multiplied with the texture value to allow for texture to provide surface detail.
+// @TODO texture should be regenerated whenever headColor/LidColor values change to allow for networking, though arguably these will eventually be fetched from a users "profile" anywya, so might not be worth trying to network through NAF.
+AFRAME.registerComponent("avatar-customization", {
+  schema: {
+    headColor: { type: "color", default: null },
+    lidColor: { type: "color", default: null }
+  },
+
+  init: function() {
+    const colors = Please.make_color({
+      format: "rgb-string",
+      colors_returned: 2
+    });
+
+    this.colorMap = new Map();
+    this.colorMap.set(128, this.data.headColor || new THREE.Color(colors[0]));
+    this.colorMap.set(115, this.data.lidColor || new THREE.Color(colors[1]));
+
+    this.el.addEventListener("model-loaded", () => {
+      const map = this.el.object3D.getObjectByName("DodecAvatar_Head_0")
+        .material.map;
+      const img = map.image;
+
+      const canvas = document.createElement("canvas");
+      canvas.width = img.width;
+      canvas.height = img.height;
+      const ctx = canvas.getContext("2d");
+      ctx.drawImage(img, 0, 0, img.width, img.height);
+      const imageData = ctx.getImageData(0, 0, img.width, img.height);
+      const pixelData = imageData.data;
+
+      for (let i = 0; i < pixelData.length; i += 4) {
+        // @TODO check alpha channel and multiply colors to preserver surface detail
+        if (this.colorMap.has(pixelData[i])) {
+          const color = this.colorMap.get(pixelData[i]);
+          pixelData[i] = color.r * 255;
+          pixelData[i + 1] = color.g * 255;
+          pixelData[i + 2] = color.b * 255;
+        }
+      }
+      ctx.putImageData(imageData, 0, 0);
+      map.image = canvas;
+    });
+  }
+});
diff --git a/src/index.js b/src/index.js
index e1f40da052dd2b4d1952513e8fefbdc9a9c1065e..1c226e589b3834306ff95f6863abe2dc1065365d 100644
--- a/src/index.js
+++ b/src/index.js
@@ -6,6 +6,7 @@ import "./components/snap-rotation";
 import "./components/mute-mic";
 import "./components/audio-feedback";
 import "./components/nametag-transform";
+import "./components/avatar-customization";
 
 import { generateName } from "./utils";
 
diff --git a/yarn.lock b/yarn.lock
index f7eeb63a0b1fa7ef544bb06a86574af7ea0904bf..5a60e56feafd47abeaacb4bbe63c2799df114c51 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2090,6 +2090,10 @@ pify@^2.0.0:
   version "2.3.0"
   resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
 
+pleasejs@^0.4.2:
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/pleasejs/-/pleasejs-0.4.2.tgz#aaaa1a5fa6902518de7e51e3c63b5f537f823164"
+
 preserve@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"