diff --git a/js/artwork.js b/js/artwork.js
new file mode 100644
index 0000000..3e6d7a9
--- /dev/null
+++ b/js/artwork.js
@@ -0,0 +1,169 @@
+"use strict";
+
+function svgURI(svg) {
+ return "data:image/svg+xml," + encodeURIComponent(svg);
+}
+
+function gid(seed, name) {
+ return "g" + name + seed;
+}
+
+function artSVG(palette, seed, pattern) {
+ const rnd = seeded(seed);
+ const c1 = palette[0], c2 = palette[1], c3 = palette[2];
+ const id = gid(seed, pattern);
+ let inner = "";
+
+ if (pattern === "mesh") {
+ inner = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ if (pattern === "waves") {
+ let bands = "";
+ for (let i = 0; i < 6; i++) {
+ const y = 60 + i * 78 + rnd() * 24;
+ const amp = 26 + rnd() * 30;
+ const col = [c1, c2, c3, "#ffffff"][i % 4];
+ const op = 0.75 - i * 0.09;
+ bands += ``;
+ }
+ inner = `${bands}`;
+ }
+
+ if (pattern === "arcs") {
+ let arcs = "";
+ const cx = 120 + rnd() * 200, cy = 120 + rnd() * 200;
+ for (let i = 0; i < 9; i++) {
+ const r = 40 + i * 42 + rnd() * 20;
+ const col = [c1, c2, c3, "#ffffff"][i % 4];
+ arcs += ``;
+ }
+ inner = `
+
+
+ ${arcs}`;
+ }
+
+ if (pattern === "rings") {
+ let rings = "";
+ for (let i = 0; i < 7; i++) {
+ const r = 60 + i * 55;
+ const col = [c1, c2, c3, "#ffffff"][i % 4];
+ rings += ``;
+ }
+ inner = `
+
+
+ ${rings}`;
+ }
+
+ if (pattern === "stripes") {
+ inner = `
+
+
+
+ ${[0, 1, 2, 3, 4, 5, 6].map((i) => ``).join("")}
+ ${[0, 1, 2, 3].map((i) => ``).join("")}
+ `;
+ }
+
+ if (pattern === "sparkle") {
+ let dots = "";
+ for (let x = 20; x < 512; x += 26) {
+ for (let y = 20; y < 512; y += 26) {
+ const r = rnd();
+ if (r < 0.28) continue;
+ const size = 1 + rnd() * 5;
+ const col = [c1, c2, c3, "#ffffff"][Math.floor(rnd() * 4)];
+ dots += ``;
+ }
+ }
+ inner = `
+
+
+ ${dots}`;
+ }
+
+ if (pattern === "orbital") {
+ let stars = "";
+ for (let i = 0; i < 70; i++) {
+ stars += ``;
+ }
+ inner = `
+ ${stars}
+
+
+
+
+
+
+
+
+ `;
+ }
+
+ if (pattern === "grain") {
+ let grains = "";
+ for (let i = 0; i < 260; i++) {
+ grains += ``;
+ }
+ inner = `
+
+
+ ${grains}
+ `;
+ }
+
+ return ``;
+}
+
+const Art = {
+ album(album) {
+ const pattern = ["mesh", "waves", "arcs", "rings", "stripes", "sparkle", "orbital", "grain"][album.seed % 8];
+ return svgURI(artSVG(album.palette, album.seed, pattern));
+ },
+ song(song) {
+ return Art.album(D.album(song.albumId));
+ },
+ mix(mix) {
+ return svgURI(artSVG(mix.palette, hashStr(mix.id) % 997, ["mesh", "orbital", "sparkle", "rings"][hashStr(mix.id) % 4]));
+ },
+ station(station) {
+ return svgURI(artSVG(station.palette, station.seed * 7 + 1, ["arcs", "sparkle", "waves", "stripes"][station.seed % 4]));
+ },
+ artist(artist) {
+ const initials = artist.name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
+ const rnd = seeded(hashStr(artist.id));
+ const r1 = 30 + rnd() * 200, r2 = 30 + rnd() * 200;
+ const svg = ``;
+ return svgURI(svg);
+ },
+ placeholder() {
+ return svgURI(``);
+ }
+};