{"id":6381,"date":"2023-07-05T22:36:40","date_gmt":"2023-07-05T22:36:40","guid":{"rendered":"https:\/\/dgen.net\/0\/?p=6381"},"modified":"2024-12-24T23:46:21","modified_gmt":"2024-12-24T23:46:21","slug":"bringing-album-art-back-to-life","status":"publish","type":"post","link":"https:\/\/dgen.net\/0\/2023\/07\/05\/bringing-album-art-back-to-life\/","title":{"rendered":"Bringing album art back to life (&#8216;pi-fi&#8217;)"},"content":{"rendered":"\n<p>So, after asking Spotify to <a href=\"https:\/\/community.spotify.com\/t5\/Live-Ideas\/Desktop-Other-Option-to-maximize-album-cover-in-fullscreen\/idc-p\/5500330\/highlight\/true#M251614\">treat album art properly for over a decade<\/a> I ended up giving up and fixing it myself (and <a href=\"https:\/\/community.spotify.com\/t5\/Live-Ideas\/Desktop-Other-Option-to-maximize-album-cover-in-fullscreen\/idc-p\/5605544\/highlight\/true#M263950\">let them know<\/a>). <\/p>\n\n\n\n<p>I now have a nice 12&#8243; x 12&#8243; screen embedded in my wall that, when I&#8217;m using Spotify, gets the album art and puts it on the screen. And when it&#8217;s not in use, it defaults back to a clock \ud83d\ude42 <\/p>\n\n\n\n<p>It&#8217;s rather lovely, I must say, and creates exactly the effect I was wanting it to.<\/p>\n\n\n\n<p>And, in case you are wondering, the album cover above is from my album, <a href=\"https:\/\/open.spotify.com\/artist\/62BXz0wQ49beKWk6E9yGl9?si=qwRBghJmSwidPKrpmrEcLg\">Binary Dust<\/a> (more on that at <a href=\"https:\/\/binarydust.org\">https:\/\/binarydust.org<\/a>).<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock.jpg\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"510\" src=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-1024x510.jpg\" alt=\"\" class=\"wp-image-6383\" srcset=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-1024x510.jpg 1024w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-300x149.jpg 300w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-768x383.jpg 768w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-1536x765.jpg 1536w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-830x414.jpg 830w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-230x115.jpg 230w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-350x174.jpg 350w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock-480x239.jpg 480w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-clock.jpg 1702w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Here&#8217;s how<\/h2>\n\n\n\n<p>What kit? Not a lot&#8230; <\/p>\n\n\n\n<p>1 x very old 4:3 screen (this one only had a VGA input) which I picked up for free. It&#8217;s nicely matt, and not too bright. I suspect a more modern monitor may actually be a bit too bright and shiny.<br>1 x Raspberry Pi Model B <br>1 x HDMI to VGA cable<br>1 x bit of old ply cut as a square-crop frame and painted with blackboard paint and a couple of strips of single-sided sticky foam tape stuck on the verticals (not essential, but helps mask the edges of the screen from light bleed)<br>10 x patience because I&#8217;m well out of practice on code <\/p>\n\n\n\n<p>Cutting the square was ok (easiest if you have a multi-tool electric saw). Patience was needed for the code&#8230; <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up your Pi to be in kiosk mode<\/h2>\n\n\n\n<p>Make sure your Pi is up-to-date and installed with the Chromium browser. You&#8217;ll also need to install an extra so that you can refresh the browser from the command line, so add <strong><em>xdotool<\/em><\/strong> with<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sudo apt-get install xdotool<\/pre>\n\n\n\n<p>and, we&#8217;ll need a version of <strong>ping <\/strong>that can wake up devices so that <strong>arp<\/strong> can find them, called <strong>fping<\/strong> <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">sudo apt-get install fping<\/pre>\n\n\n\n<p>Then we need to set it so that it boots directly into kiosk mode.<\/p>\n\n\n\n<p>Edit the file <strong><em>\/etc\/xdg\/openbox\/autostart <\/em><\/strong>as follows<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"># Disable any form of screen saver \/ screen blanking \/ power management\n xset s off\n xset s noblank\n xset -dpms\n\n# # Allow quitting the X server with CTRL-ATL-Backspace\n setxkbmap -option terminate:ctrl_alt_bksp\n\n# # get the IP of the amp\n\/home\/pi\/screen\/scan.sh\n\n# # Start Chromium in kiosk mode\n sed -i 's\/\"exited_cleanly\":false\/\"exited_cleanly\":true\/' ~\/.config\/chromium\/'Local State'\n sed -i 's\/\"exited_cleanly\":false\/\"exited_cleanly\":true\/; s\/\"exit_type\":\"[^\"]\\+\"\/\"exit_type\":\"Normal\"\/' ~\/.config\/chromiu\nm\/Default\/Preferences\n  chromium-browser --disable-infobars --kiosk <strong>\/home\/pi\/screen\/album-cover.html <\/strong><\/pre>\n\n\n\n<p>Note the path at the end to where your HTML file is, and the call to scan, which gets the IP address of the amp based on its unique MAC (hardware) address on boot and drops it into a file called <strong>hifi.ip<\/strong>. Note that to get the MAC address of the amp (<em>00:06:78:14:0d:fa<\/em>), I ran <strong><em><a href=\"https:\/\/man7.org\/linux\/man-pages\/man8\/arp.8.html\">arp -a<\/a><\/em> <\/strong>from my Mac laptop and looked through for the IPs to find the one that matched the one displayed on the amp itself.<\/p>\n\n\n\n<p><strong>scan.sh<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/bash\n\ncd \/home\/pi\/screen\nhifi=`cat hifi.ip` # get the last IP used\nup=`ping -c 1 $hifi  &amp;> \/dev\/null &amp;&amp; echo yes || echo no` # check if it's alive\n\nif test \"$up\" = \"no\" ; then\n# sometimes arp only recognises pre-existing things on the network if they've done something first. This hack uses fping to wake up everything on the subnet so that arp can find it \nfping -gaq 192.168.1.0\/24 &amp;> \/dev\/null \n\n# we expect arp does actually find the new IP, filtered out using the hifi's MAC address, and drop it into a file\narp -a | grep 00:06:78:14:0d:fa | awk '{print $2}' | sed -e 's\/(\/\/g' | sed -e 's\/)\/\/g' > \/home\/pi\/screen\/hifi.ip\nfi<\/pre>\n\n\n\n<p>Now some fun. If you are using an old monitor, you need to force it to be VGA. This tripped me up for a while as I could boot the pi without the HDMI cable plugged in, then plug the cable in and it&#8217;d work, but if I booted it with the cable in from the start, it wouldn&#8217;t. Also fun fact: you can&#8217;t plug the HDMI output of your Mac into VGA (eg. using an adapted cable) &lt;grumble grumble DRM&gt;. Mac blocks it. <\/p>\n\n\n\n<p>So, in <strong><em>\/boot\/config.txt<\/em><\/strong> you need to set <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">hdmi_group=1:\nhdmi_mode=1<\/pre>\n\n\n\n<p>Or, depending on your monitor, fiddle around with it. It also lets you rotate the screen (which I&#8217;ve used in other dashboard-like applications when mounting monitors on their sides). <\/p>\n\n\n\n<p>You&#8217;ll also want to get rid of the cursor, so edit your &nbsp;<strong><em>~\/.bash_profile<\/em><\/strong>&nbsp; to <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">[[ -z $DISPLAY &amp;&amp; $XDG_VTNR -eq 1 ]] &amp;&amp; startx -- -nocursor\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Getting the album image<\/h2>\n\n\n\n<p>I was expecting to have to go get the album art via the Spotify API, and slightly dreading that as my coding skills are now terrible, and so I was hoping to just ugly-hack it in shell scripts (and not have to open sockets and auth and APIs and such)&#8230; and &#8230; I lucked out as my amplifier (Marantz sr7007) has Spotify and its own (albeit truly dreadful) web interface. <\/p>\n\n\n\n<p>This meant I could find a rendered image when the amp is on, at <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">http:\/\/<em><strong>[IP ADDRESS OF MY AMP]<\/strong><\/em>\/NetAudio\/art.asp-jpg<\/pre>\n\n\n\n<p>(the IP sometimes varies, see above).<\/p>\n\n\n\n<p>So, hurra! Let&#8217;s get to the hack that works for me.<\/p>\n\n\n\n<p>We need to get the image, but not have the browser screen refresh\/flash every 2 seconds as that&#8217;ll be more than distracting. So, here&#8217;s a shell script that gets the image, tests if the image has changed from the last time it checked (using the <strong><em><a href=\"https:\/\/en.wikipedia.org\/wiki\/Md5sum\">md5sum<\/a><\/em><\/strong> of the current live image vs this latest one), and if it has, refreshes the screen:<\/p>\n\n\n\n<p><strong>test-refresh.sh<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/sh\ncd \/home\/pi\/screen \nhifi=`cat hifi.ip` # gets the IP address of the amp\nalbumart=\"http:\/\/$hifi\/NetAudio\/art.asp-jpg\"\ncurl $albumart > nowtmp.jpg # gets the image, saves locally\nmv nowtmp.jpg now.jpg # do as a two-step in case download latency leads to partial-download comparisons\n\nx=`md5sum now.jpg  | awk '{print $1}'` # gets a unique number for this image\ny=`md5sum live.jpg | awk '{print $1}'` # gets a unique number for live image\nif test \"$x\"  != \"$y\"; then # if they're not the same\n  cp now.jpg live.jpg. # update the live image\n  .\/refresh.sh # and refresh the browser\nfi<\/pre>\n\n\n\n<p>Remember the autostart has already loaded the web page, so refresh just needs to effectively hit reload if the image has changed. Here&#8217;s a script to do that:<\/p>\n\n\n\n<p><strong>refresh.sh<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/sh \nexport DISPLAY=:0\nexport XAUTHORITY=\/home\/pi\/.Xauthority\nxdotool getactivewindow\nxdotool key F5<\/pre>\n\n\n\n<p>Yup, it does the equivalent of hit F5 on the keyboard.<\/p>\n\n\n\n<p>So, now to the web page, <strong>album-cover.html<\/strong> which puts the image in the right place.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;head&gt; \n    &lt;link href=\"album-cover.css\" rel=\"stylesheet\" type=\"text\/css\" \/&gt;  \n&lt;\/head&gt;\n&lt;body style=\"background-color: black;\"&gt;\n&lt;div id='outerdiv'&gt;\n&lt;iframe src=\"live.jpg\" id='inneriframe' scrolling=no&gt;&lt;\/iframe&gt;\n&lt;\/div&gt;\n&lt;\/body&gt;<\/pre>\n\n\n\n<p>And the CSS is&#8230; interesting. Because [reasons that I&#8217;ve stopped trying to work out] &#8230;something about the 4:3 ratio, the HDMI settings, shoddy CSS coding, the way that chromium works on a Pi, who knows, I had to hack the CSS on the Pi to be different to my testing on my Mac (which worked fine without tweaking\/stretching\/etc). Much faffing around later&#8230; this:<\/p>\n\n\n\n<p><strong>album-cover.css<\/strong> <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">@charset \"utf-8\";  \n#outerdiv\n{\nwidth:600px;\nheight:600px;\noverflow:hidden;\nposition:relative;\nborder-width:0px;\nborder-style:none;\npadding: 0px;\ntext-align: center;\nalign:center;\nmargin-left: auto;\nmargin-right: auto;\nmargin-top: auto;\nmargin-bottom: auto;\nbackground-color: black;\nobject-fit: contain;\n}\n\n#inneriframe\n{\nposition:absolute;\ntop:0px;\nleft:32px; \/\/ manual tweak to the edge of the wooden frame\nwidth:768px;\nheight:768px;\nborder-width:0px;\nalign:center;\nmargin-left: auto;\nmargin-right: auto;\nmargin-top: auto;\nmargin-bottom: auto;\n\/\/ now we unsquish the image because the Pi squished it somehow \nobject-fit: contain;-webkit-transform: scale(.90, .81); \n-webkit-transform-origin: 0 0;\n}<\/pre>\n\n\n\n<p>And so, now we&#8217;ve got the image, and managed to put it on the screen, and -just about- got it to fill a square with a square image.<\/p>\n\n\n\n<p>Now I wanted to test if the amp was on\/off and replace the image with a clock if Spotify wasn&#8217;t on. Nice blunt instrument here:<\/p>\n\n\n\n<p><strong>up-amp.sh<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/bash\ncd \/home\/pi\/screen\/\nhifi=`cat hifi.ip`\nup=`ping -c 1 $hifi &amp;> \/dev\/null &amp;&amp; echo yes || echo no` # is it up?\nif test \"$up\" = \"yes\" ; then # if it's up \n if test `cat status.flag` = \"no\" ; then  # update the flag and the cover\n\t echo \"yes\" > status.flag\n  \t cp album-cover-live-safe.html album-cover.html\n         .\/refresh.sh\n fi\t \nelse # if it's not up\n if test `cat status.flag` = \"yes\" ; then # update flag and switch to clock\t \n\t echo \"no\" > status.flag\n \t cp clock-analogue.html album-cover.html\n         .\/refresh.sh\n fi\nfi\nsleep 5  \n<\/pre>\n\n\n\n<p>The code for the clock is at the bottom of this post &#8211; there are loads of examples out there.<\/p>\n\n\n\n<p>Now, finally, we need to get the Pi to keep checking what&#8217;s happening on the amp, and since the Pi isn&#8217;t doing anything else, and I&#8217;m lazy, we&#8217;ll get a script to test\/get the image from the amp every 2 seconds. But because <em><strong><a href=\"https:\/\/en.wikipedia.org\/wiki\/Cron\">cron<\/a><\/strong><\/em> doesn&#8217;t do less than 1-minute resolution, I wrote a script that can be run every minute that then runs the checking command every 5 seconds for a minute.  <\/p>\n\n\n\n<p><strong>albumcron.sh&nbsp;<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/sh\ni=0\nwhile [ $i -lt 12 ]; do # run 12 times at five-second intervals in one minute\n \/home\/pi\/screen\/test-refresh.sh &amp; # test for a new image\n sleep 5 # wait two seconds\n i=$(( i + 1 )) \ndone<\/pre>\n\n\n\n<p>And so, finally, we can ask <strong><em>cron<\/em><\/strong> to do something!  Run<\/p>\n\n\n\n<p>&gt; <strong>crontab -e<\/strong> <\/p>\n\n\n\n<p>And enter these two lines<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">* * * * * \/home\/pi\/screen\/albumcron.sh &gt; \/dev\/null 2&gt;&amp;1 # two-sec art checks\n* * * * * \/home\/pi\/screen\/up-amp.sh &gt; \/dev\/null 2&gt;&amp;1 # one minute amp checks<\/pre>\n\n\n\n<p>This checks if the amp is <strong>on<\/strong> once a minute and, if so, refreshes everything. The Pi also tries to get a new image every two seconds while it&#8217;s on and, no, I&#8217;ve not written* a thing to make it not do that when the amp is off because, unfortunately for it, it now all just works\u2014in spite of shonky code and inefficient processes\u2014and I have music to listen to and, frankly, no one cares (apart from my ego).   Good enough.<\/p>\n\n\n\n<p>*update: turns out I did as it was simple: just add <em><strong>if test `cat status.flag` = &#8220;yes&#8221; ; then<\/strong><\/em> around the <strong>albumcron.sh<\/strong> <em>while<\/em> statement. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The clock thing<\/h2>\n\n\n\n<p>I tried a few different ones, but liked the way this looks on the wall and it has a nice &#8216;glitchy&#8217; second hand that makes it feel analogue. <\/p>\n\n\n\n<p>Source: <a href=\"https:\/\/codepen.io\/rodnylobos\/pen\/OJmZOx\">https:\/\/codepen.io\/rodnylobos\/pen\/OJmZOx<\/a> and my edited one below to faff move it into the centre, size is to as big as it&#8217;ll go, and set the background colour to match the blackboard paint hue.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><a href=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock.png\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-1024x984.png\" alt=\"\" class=\"wp-image-6389\" width=\"247\" height=\"237\" srcset=\"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-1024x984.png 1024w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-300x288.png 300w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-768x738.png 768w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-830x797.png 830w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-230x221.png 230w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-350x336.png 350w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock-480x461.png 480w, https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/clock.png 1070w\" sizes=\"auto, (max-width: 247px) 100vw, 247px\" \/><\/a><\/figure>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;!DOCTYPE html&gt;\n&lt;html &gt;\n&lt;head&gt;\n  &lt;meta charset=\"UTF-8\"&gt;\n  &lt;title&gt;Clock Snap&lt;\/title&gt;\n  &lt;script src=\"http:\/\/s.codepen.io\/assets\/libs\/modernizr.js\" type=\"text\/javascript\"&gt;&lt;\/script&gt;\n\n\n&lt;style type=\"text\/css\"&gt;\n\n\thtml, body, p {\n  background: #444;\n  background-color: #444;\n\n  margin: 5px;\n  height: 100%; overflow: hidden\n}\nsvg{\n  display: block;\n  margin:  -150px 0px 0px -120px;\n  background-color: #444;\n\n}\n&lt;\/style&gt;\n\n&lt;\/head&gt;\n\n&lt;body&gt;\n  &lt;svg version=\"1.1\" id=\"clock\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" xmlns:xlink=\"http:\/\/www.w3.org\/1999\/xlink\" x=\"0\" y=\"0\"\n\t width=\"900px\" height=\"900px\" viewBox=\"0 0 500 600\" \n\t xml:space=\"preserve\"&gt;\n&lt;circle id=\"face\" fill=\"#F4F3ED\" cx=\"243.869\" cy=\"250.796\" r=\"130.8\"\/&gt;\n&lt;path id=\"rim\" fill=\"#383838\" d=\"M243.869,101.184c-82.629,0-149.612,66.984-149.612,149.612c0,82.629,66.983,149.612,149.612,149.612\n\tS393.48,333.425,393.48,250.796S326.498,101.184,243.869,101.184z M243.869,386.455c-74.922,0-135.659-60.736-135.659-135.659\n\tc0-74.922,60.737-135.659,135.659-135.659c74.922,0,135.658,60.737,135.658,135.659\n\tC379.527,325.719,318.791,386.455,243.869,386.455z\"\/&gt;\n&lt;g id=\"inner\"&gt;\n\t&lt;g opacity=\"0.2\"&gt;\n\t\t&lt;path fill=\"#C4C4C4\" d=\"M243.869,114.648c-75.748,0-137.154,61.406-137.154,137.153c0,75.749,61.406,137.154,137.154,137.154\n\t\t\tc75.748,0,137.153-61.405,137.153-137.154C381.022,176.054,319.617,114.648,243.869,114.648z M243.869,382.56\n\t\t\tc-72.216,0-130.758-58.543-130.758-130.758s58.542-130.758,130.758-130.758c72.216,0,130.758,58.543,130.758,130.758\n\t\t\tS316.085,382.56,243.869,382.56z\"\/&gt;\n\t&lt;\/g&gt;\n\t&lt;g&gt;\n\t\t&lt;path fill=\"#282828\" d=\"M243.869,113.637c-75.748,0-137.154,61.406-137.154,137.153c0,75.749,61.406,137.154,137.154,137.154\n\t\t\tc75.748,0,137.153-61.405,137.153-137.154C381.022,175.043,319.617,113.637,243.869,113.637z M243.869,381.548\n\t\t\tc-72.216,0-130.758-58.542-130.758-130.757c0-72.216,58.542-130.758,130.758-130.758c72.216,0,130.758,58.543,130.758,130.758\n\t\t\tC374.627,323.005,316.085,381.548,243.869,381.548z\"\/&gt;\n\t&lt;\/g&gt;\n&lt;\/g&gt;\n&lt;g id=\"markings\"&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"243.5\" y1=\"139\" x2=\"243.5\" y2=\"133\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"231.817\" y1=\"139.651\" x2=\"231.19\" y2=\"133.684\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"220.266\" y1=\"141.52\" x2=\"219.018\" y2=\"135.65\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"208.973\" y1=\"144.585\" x2=\"207.119\" y2=\"138.879\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"198.063\" y1=\"148.814\" x2=\"195.623\" y2=\"143.333\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"187.655\" y1=\"154.161\" x2=\"184.655\" y2=\"148.965\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"177.862\" y1=\"160.566\" x2=\"174.335\" y2=\"155.712\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"168.792\" y1=\"167.96\" x2=\"164.778\" y2=\"163.501\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"160.545\" y1=\"176.262\" x2=\"156.087\" y2=\"172.246\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"153.211\" y1=\"185.379\" x2=\"148.358\" y2=\"181.852\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"146.871\" y1=\"195.214\" x2=\"141.675\" y2=\"192.213\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"141.593\" y1=\"205.658\" x2=\"136.112\" y2=\"203.216\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"137.436\" y1=\"216.596\" x2=\"131.729\" y2=\"214.741\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"134.445\" y1=\"227.909\" x2=\"128.576\" y2=\"226.66\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"132.653\" y1=\"239.472\" x2=\"126.685\" y2=\"238.843\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"132.079\" y1=\"251.16\" x2=\"126.079\" y2=\"251.158\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"132.73\" y1=\"262.843\" x2=\"126.762\" y2=\"263.468\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"134.598\" y1=\"274.395\" x2=\"128.729\" y2=\"275.64\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"137.664\" y1=\"285.688\" x2=\"131.958\" y2=\"287.539\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"141.893\" y1=\"296.598\" x2=\"136.412\" y2=\"299.035\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"147.24\" y1=\"307.006\" x2=\"142.043\" y2=\"310.004\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"153.645\" y1=\"316.799\" x2=\"148.791\" y2=\"320.323\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"161.04\" y1=\"325.868\" x2=\"156.58\" y2=\"329.881\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"169.341\" y1=\"334.115\" x2=\"165.325\" y2=\"338.572\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"178.459\" y1=\"341.449\" x2=\"174.931\" y2=\"346.302\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"188.294\" y1=\"347.789\" x2=\"185.292\" y2=\"352.984\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"198.738\" y1=\"353.066\" x2=\"196.295\" y2=\"358.548\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"209.676\" y1=\"357.223\" x2=\"207.82\" y2=\"362.93\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"220.989\" y1=\"360.214\" x2=\"219.739\" y2=\"366.084\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"232.552\" y1=\"362.006\" x2=\"231.922\" y2=\"367.975\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"244.239\" y1=\"362.58\" x2=\"244.237\" y2=\"368.582\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"255.921\" y1=\"361.93\" x2=\"256.547\" y2=\"367.898\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"267.472\" y1=\"360.062\" x2=\"268.719\" y2=\"365.932\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"278.765\" y1=\"356.996\" x2=\"280.619\" y2=\"362.703\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"289.675\" y1=\"352.767\" x2=\"292.116\" y2=\"358.248\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"300.083\" y1=\"347.42\" x2=\"303.083\" y2=\"352.616\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"309.876\" y1=\"341.015\" x2=\"313.403\" y2=\"345.869\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"318.946\" y1=\"333.621\" x2=\"322.96\" y2=\"338.08\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"327.193\" y1=\"325.319\" x2=\"331.651\" y2=\"329.334\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"334.527\" y1=\"316.201\" x2=\"339.38\" y2=\"319.728\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"340.868\" y1=\"306.367\" x2=\"346.063\" y2=\"309.367\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"346.146\" y1=\"295.924\" x2=\"351.626\" y2=\"298.364\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"350.303\" y1=\"284.986\" x2=\"356.008\" y2=\"286.84\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"353.294\" y1=\"273.673\" x2=\"359.162\" y2=\"274.92\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"355.087\" y1=\"262.11\" x2=\"361.052\" y2=\"262.737\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"356\" y1=\"250.5\" x2=\"362\" y2=\"250.5\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"355.355\" y1=\"238.781\" x2=\"361.323\" y2=\"238.153\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"353.489\" y1=\"227.193\" x2=\"359.359\" y2=\"225.945\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"350.422\" y1=\"215.864\" x2=\"356.129\" y2=\"214.01\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"346.188\" y1=\"204.918\" x2=\"351.669\" y2=\"202.477\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"340.833\" y1=\"194.474\" x2=\"346.029\" y2=\"191.474\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"334.415\" y1=\"184.647\" x2=\"339.269\" y2=\"181.12\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"327.004\" y1=\"175.545\" x2=\"331.463\" y2=\"171.529\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"318.684\" y1=\"167.268\" x2=\"322.699\" y2=\"162.807\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"309.543\" y1=\"159.905\" x2=\"313.07\" y2=\"155.049\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"299.684\" y1=\"153.538\" x2=\"302.683\" y2=\"148.34\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"289.212\" y1=\"148.237\" x2=\"291.652\" y2=\"142.753\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"278.245\" y1=\"144.059\" x2=\"280.097\" y2=\"138.351\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"266.9\" y1=\"141.05\" x2=\"268.145\" y2=\"135.179\"\/&gt;\n\t&lt;line fill=\"none\" stroke=\"#3F3F3F\" stroke-miterlimit=\"10\" x1=\"255.302\" y1=\"139.244\" x2=\"255.927\" y2=\"133.275\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"247.391,133 243.5,141.05 239.609,133 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"188.022,147.021 188.677,155.938 181.283,150.912 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"143.617,188.848 148.643,196.243 139.726,195.588 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"126.074,247.273 134.125,251.165 126.076,255.056 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"140.095,306.644 149.013,305.988 143.988,313.382 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"181.922,351.049 189.318,346.022 188.663,354.938 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"240.349,368.591 244.24,360.54 248.13,368.589 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"299.718,354.569 299.062,345.652 306.457,350.677 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"344.123,312.742 339.096,305.348 348.012,306.002 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"362,254.316 353.951,250.426 362,246.534 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"347.934,194.779 339.018,195.435 344.042,188.04 \t\"\/&gt;\n\t&lt;polygon fill=\"#3F3F3F\" points=\"305.984,150.252 298.59,155.277 299.244,146.361 \t\"\/&gt;\n\t&lt;rect x=\"282\" y=\"152.98\" fill=\"none\" width=\"17.366\" height=\"27.947\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 282 174.4307)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;1&lt;\/text&gt;\n\t&lt;rect x=\"320.699\" y=\"188.474\" fill=\"none\" width=\"17.202\" height=\"26.267\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 320.6987 209.9229)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;2&lt;\/text&gt;\n\t&lt;rect x=\"335.04\" y=\"238.872\" fill=\"none\" width=\"21.03\" height=\"24.585\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 335.0396 260.3213)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;3&lt;\/text&gt;\n\t&lt;rect x=\"319.699\" y=\"290.242\" fill=\"none\" width=\"17.202\" height=\"23.557\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 319.6987 311.6914)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;4&lt;\/text&gt;\n\t&lt;rect x=\"284.5\" y=\"323.319\" fill=\"none\" width=\"19.212\" height=\"22.511\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 284.5 344.7695)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;5&lt;\/text&gt;\n\t&lt;rect x=\"235.552\" y=\"336.08\" fill=\"none\" width=\"19.938\" height=\"24.15\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 235.5522 357.5293)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;6&lt;\/text&gt;\n\t&lt;rect x=\"189.373\" y=\"322.319\" fill=\"none\" width=\"19.673\" height=\"25.003\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 189.3726 343.7695)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;7&lt;\/text&gt;\n\t&lt;rect x=\"151.066\" y=\"287.539\" fill=\"none\" width=\"17.726\" height=\"25.203\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 151.0664 308.9883)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;8&lt;\/text&gt;\n\t&lt;rect x=\"136.392\" y=\"241.25\" fill=\"none\" width=\"20.696\" height=\"22.348\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 136.3916 262.6992)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;9&lt;\/text&gt;\n\t&lt;rect x=\"149.066\" y=\"191.474\" fill=\"none\" width=\"36.554\" height=\"27.122\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 149.0664 212.9229)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;10&lt;\/text&gt;\n\t&lt;rect x=\"184.967\" y=\"158.518\" fill=\"none\" width=\"36.021\" height=\"27.13\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 184.9673 179.9668)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;11&lt;\/text&gt;\n\t&lt;rect x=\"225.723\" y=\"144.514\" fill=\"none\" width=\"37.029\" height=\"29.25\"\/&gt;\n\t&lt;text transform=\"matrix(1 0 0 1 225.7227 165.9639)\" fill=\"#303030\" font-family=\"'Futura-Medium'\" font-size=\"26\"&gt;12&lt;\/text&gt;\n&lt;\/g&gt;\n&lt;path id=\"hours\" fill=\"#3A3A3A\" d=\"M242.515,270.21c-0.44,0-0.856-0.355-0.926-0.79l-3.156-19.811c-0.069-0.435-0.103-1.149-0.074-1.588\n\tl4.038-62.009c0.03-0.439,0.414-0.798,0.854-0.798h0.5c0.44,0,0.823,0.359,0.852,0.798l4.042,62.205\n\tc0.028,0.439-0.015,1.152-0.097,1.584l-3.712,19.623c-0.082,0.433-0.508,0.786-0.948,0.786H242.515z\"\/&gt;\n&lt;path id=\"minutes\" fill=\"#3A3A3A\" d=\"M247.862,249.75l-2.866,24.244c-0.099,1.198-0.498,2.18-1.497,2.179c-0.999,0-1.397-0.98-1.498-2.179\n\t\t\tl-2.861-24.508c-0.099-1.199,3.479-93.985,3.479-93.985c0.036-1.201-0.117-2.183,0.881-2.183c0.999,0,0.847,0.982,0.882,2.183\n\t\t\tL247.862,249.75z\"\/&gt;\n&lt;g id=\"seconds\"&gt;\n\t&lt;line fill=\"none\" stroke=\"#BF4116\" stroke-miterlimit=\"10\" x1=\"243.5\" y1=\"143\" x2=\"243.5\" y2=\"266\"\/&gt;\n\t&lt;circle fill=\"none\" stroke=\"#BF4116\" stroke-miterlimit=\"10\" cx=\"243.5\" cy=\"271\" r=\"5\"\/&gt;\n\t&lt;circle fill=\"#BF4116\" cx=\"243.5\" cy=\"251\" r=\"3.917\"\/&gt;\n&lt;\/g&gt;\n&lt;\/svg&gt;\n  &lt;script src='http:\/\/cdnjs.cloudflare.com\/ajax\/libs\/snap.svg\/0.2.0\/snap.svg-min.js'&gt;&lt;\/script&gt;\n\n&lt;\/body&gt;\n&lt;\/html&gt;\n\n\n\n\n&lt;script type=\"text\/javascript\"&gt;\nvar s = Snap(document.getElementById(\"clock\"));\n\n\t\tvar seconds = s.select(\"#seconds\"),\n\t\t    minutes = s.select(\"#minutes\"),\n\t\t    hours   = s.select(\"#hours\"),\n\t\t    rim     = s.select(\"#rim\"), \n\t\t    face    = {\n\t\t      elem: s.select(\"#face\"),\n\t\t      cx: s.select(\"#face\").getBBox().cx,\n\t\t      cy: s.select(\"#face\").getBBox().cy,\n\t\t    },\n\t\t    angle   = 0,\n\t\t    easing = function(a) {\n\t\t      return a==!!a?a:Math.pow(4,-10*a)*Math.sin((a-.075)*2*Math.PI\/.3)+1;\n\t\t    };\n\n\t\tvar sshadow = seconds.clone(),\n\t\t\tmshadow = minutes.clone(),\n\t\t\thshadow = hours.clone(),\n\t\t\trshadow = rim.clone(),\n\t\t\tshadows = [sshadow, mshadow, hshadow];\n\n\t\t\/\/Insert shadows before their respective opaque pals\n\t\tseconds.before(sshadow);\n\t\tminutes.before(mshadow);\n\t\thours.before(hshadow);\n\t\trim.before(rshadow);\n\n\t\t\/\/Create a filter to make a blurry black version of a thing\n\t\tvar filter = Snap.filter.blur(0.1) + Snap.filter.brightness(0);\n\n\t\t\/\/Add the filter, shift and opacity to each of the shadows\n\t\tshadows.forEach(function(el){\n\t\t\tel.attr({\n\t\t\t\ttransform: \"translate(0, 2)\",\n\t\t\t\topacity: 0.2,\n\t\t\t\tfilter: s.filter(filter)\n\t\t\t});\n\t\t})\n\n\t\trshadow.attr({\n\t\t\ttransform: \"translate(0, 8) \",\n      opacity: 0.5,\n\t\t\tfilter: s.filter(Snap.filter.blur(0, 8)+Snap.filter.brightness(0)),\n\t\t})\n\n\t\tfunction update() {\n\t\t  var time = new Date();\n\t\t  setHours(time);\n\t\t  setMinutes(time);\n\t\t  setSeconds(time);\n\t\t}\n\n\t\tfunction setHours(t) {\n\t\t  var hour = t.getHours();\n\t\t  hour %= 12;\n\t\t  hour += Math.floor(t.getMinutes()\/10)\/6;\n\t\t  var angle = hour*360\/12;\n\t\t  hours.animate(\n\t\t    {transform: \"rotate(\"+angle+\" 244 251)\"},\n\t\t    100,\n\t\t    mina.linear,\n\t\t    function(){\n\t\t      if (angle === 360){\n\t\t        hours.attr({transform: \"rotate(\"+0+\" \"+face.cx+\" \"+face.cy+\")\"});\n\t\t        hshadow.attr({transform: \"translate(0, 2) rotate(\"+0+\" \"+face.cx+\" \"+face.cy+2+\")\"});\n\t\t      }\n\t\t    }\n\t\t  );\n\t\t  hshadow.animate(\n\t\t    {transform: \"translate(0, 2) rotate(\"+angle+\" \"+face.cx+\" \"+face.cy+2+\")\"},\n\t\t    100,\n\t\t    mina.linear\n\t\t  );\n\t\t}\n\t\tfunction setMinutes(t) {\n\t\t  var minute = t.getMinutes();\n\t\t  minute %= 60;\n\t\t  minute += Math.floor(t.getSeconds()\/10)\/6;\n\t\t  var angle = minute*360\/60;\n\t\t  minutes.animate(\n\t\t    {transform: \"rotate(\"+angle+\" \"+face.cx+\" \"+face.cy+\")\"},\n\t\t    100,\n\t\t    mina.linear,\n\t\t    function(){\n\t\t      if (angle === 360){\n\t\t        minutes.attr({transform: \"rotate(\"+0+\" \"+face.cx+\" \"+face.cy+\")\"});\n\t\t        mshadow.attr({transform: \"translate(0, 2) rotate(\"+0+\" \"+face.cx+\" \"+face.cy+2+\")\"});\n\t\t      }\n\t\t    }\n\t\t  );\n\t\t  mshadow.animate(\n\t\t    {transform: \"translate(0, 2) rotate(\"+angle+\" \"+face.cx+\" \"+face.cy+2+\")\"},\n\t\t    100,\n\t\t    mina.linear\n\t\t  );\n\t\t}\n\t\tfunction setSeconds(t) {\n\t\t  t = t.getSeconds();\n\t\t  t %= 60;\n\t\t  var angle = t*360\/60;\n\t\t  \/\/if ticking over to 0 seconds, animate angle to 360 and then switch angle to 0\n\t\t  if (angle === 0) angle = 360;\n\t\t  seconds.animate(\n\t\t    {transform: \"rotate(\"+angle+\" \"+face.cx+\" \"+face.cy+\")\"},\n\t\t    600,\n\t\t    easing,\n\t\t    function(){\n\t\t      if (angle === 360){\n\t\t        seconds.attr({transform: \"rotate(\"+0+\" \"+face.cx+\" \"+face.cy+\")\"});\n\t\t        sshadow.attr({transform: \"translate(0, 2) rotate(\"+0+\" \"+face.cx+\" \"+face.cy+2+\")\"});\n\t\t      }\n\t\t    }\n\t\t  );\n\t\t  sshadow.animate(\n\t\t    {transform: \"translate(0, 2) rotate(\"+angle+\" \"+face.cx+\" \"+face.cy+2+\")\"},\n\t\t    600,\n\t\t    easing\n\t\t  );\n\t\t}\n\t\tsetInterval(update, 1000);\t\n\n&lt;\/script&gt;\n\n<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>So, after asking Spotify to treat album art properly for over a decade I ended up giving up and fixing it myself (and let them [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":6382,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6,10,3],"tags":[],"class_list":["post-6381","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-media","category-music","category-stuff"],"jetpack_featured_media_url":"https:\/\/dgen.net\/0\/wp-content\/uploads\/2023\/07\/AlbumArt-BinaryDust.jpg","jetpack_shortlink":"https:\/\/wp.me\/pfJFK3-1EV","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/posts\/6381","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/comments?post=6381"}],"version-history":[{"count":51,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/posts\/6381\/revisions"}],"predecessor-version":[{"id":7312,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/posts\/6381\/revisions\/7312"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/media\/6382"}],"wp:attachment":[{"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/media?parent=6381"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/categories?post=6381"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dgen.net\/0\/wp-json\/wp\/v2\/tags?post=6381"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}