Build a Virtual Record Player

I built a virtual record player.  It is a virtual machine, rather than a representation. The player has components that resemble real-life components, and they act in a way that is internally valid and hues to what is expected from a real player. For instance, there can not be a status in which the Arm is not moving but sound is playing.

Screen of my virtual record player

I will detail here some of the challenges I faced, decisions made, and give credit where I received help.  The tools I used were : CSS3, PhotoShop, HTML, and JavaScript / jQuery. (the music itself was recorded by me and some friends and mixed in ProTools).

First, here is the webpage that holds my virtual record player.

One major decision was to use pixel sizing rather than percentages. That’s because I wanted to control the experience and have more options available to me regarding animation. I knew that the users would be able to zoom in as desired, so I found a size on-load that looks nice even on a large screen. (And on other hand, that I could force a zoom-out on smaller screens). That size for the vinyl record is 360px x 360px.

Using an image for the record(s) might seem a limitation. Sure, one could devote time to making a CSS circle(s) that a user could then color to their liking, and add text… and perhaps we could approximate the shadowing of the disc, but I wanted to make a record that looked good. Photoshop is where a developer would want to layout their record’s art / labeling. That’s what I did. And it saved my programming resources for the operation of the machine rather than customizing per user.

Blank PNG disc. Before Photoshop.

To design the player components I studied my own real-life player by Technics. I decided against skeuomorphism but to use flat, simple components. I wanted accuracy in placement and operation, not so much to make things look real-life. I also wanted to give users a modern experience that they would expect from all digital / websites. So certain liberties were taken but also some restrictions put in place (get to later).

So: to make a player that shows a Single (45) playing (with an adaptor) on a full-size (LP) record player.

The HTML elements are :

plate (holds disc)
spindle (inner Circle, adaptor) – uses absolute positioned CSS circle.

Let’s jump to some action and math !

The rotation of the disc and movement of the arm is based on the CSS transform:rotate property. It is manipulated through JavaScript, because the movement is connected to and must be mindful of other things going on (whereas CSS animations do not even allow for a callback function).

We want the disc to continuously rotate 360 degrees (round in circles). So I have a little function that calls itself every X milliseconds to move a degree. How do we define X ? To start, look at the name “45” or “45 rpm” .. revolutions per minute.

One revolution = 360 degrees. 45 revolutions is 360 x 45… 16,200 degree movements per minute. If a minute is 60,000 milliseconds, we get the ms interval like so : 60,000 / 16,200 .. or 3.75, which we round up to 4. Our javascript function calls itself to rotate one degree every 4 milliseconds.

The movement of the stylus confounded me, especially as I wrongly believed the arm moves 45 degrees. After more study of my Technics player, I understood that the arrangement is such that the top of the stylus to its needle, to the center of the disc is a right triangle. BUT… the arm only needs to cover an area half the length of the sides (itself) — just the actual vinyl not the label. You want to further limit the movement needed to cover that length.

One way is to slightly angle the bottom of the stylus (the wider, black shape) slightly inward (as much as you can while still assuring movement to the center of the disc). Also helpful is grasping the nature of the tone arm, which (hint : “arm” ) resembles a human joint. A lateral motion originating in the joint throws off a disproportionate motion of the limb. You have to represent this “upper rigidness.” By dividing the stylus into a couple pieces, you have leeway to give different rotation to the upper “motor” versus toneArm (and stylus as whole).  The motor / housing also provides cover to conceal the horizontal rotation. I ended up with success making the Stylus move (thus, both the motor and arm move in concert).

draft Ball and socket solution considered for Stylus movement …

To get this in place you have to do some trial and error of altering the positioning of the stylus on the disc by using the browser’s inspector and changing the CSS rotate property to derive a proper start and end point. The locations I mustered are : At Rest (-4 degrees); Begin Play (7 degrees); End Play (26 degrees). During the course of the song, the stylus moves 19 degrees.

19 degree movement during song playback.

At what rate does the stylus move? You have to calculate in the usual fashion for rate– R = t/d .. Time is the duration of the song. I track this in my global variables rather than extract it from the audio meta data. (Again, we’re only dealing with 2 songs. The owner will want to control this information; this is not an automatic player of any mp3).  I’m using 2 abbreviated versions of songs I recorded a few years ago when I pretended I was a bluesman (long story).  The Side A is 147 seconds, or 147,000 milliseconds. My script does this division of sideA.songLegth / armDistance … and tells itself what rate to move the arm during the song.

In similar fashion, once we give the user controls to Rewind and Forward, the system must know how far to move the stylus. The number you need to plug in , is the desired time interval. I decided 15 seconds. The script divides 15 into the song’s duration in seconds. This tells it what proportion of the song is being jumped (var songSeg). Then it divides that number “songSeg” Into the Total degree movement during the song (calculated earlier as 19 degrees).  In our example, 147 seconds / 15 seconds  = 9.8 song segments. Total arm movement is 19 degrees, so that gets divided by 9.8 and the script knows it will move the arm 1.94 degrees.

(Note: you have to be careful of when to use Round Up verses Round Down “floor”. For degree changes, being mindful that it will compound with each Jump, you can use the actual value, rather than have a situation where successive jumps cause the stylus to go beyond the center).


I’ve written much here, so let’s “skip” to 2 important elements of the project. First, is something cool — “flipping” the record. The rotation to reveal / hide an “underside” .. uses CSS that was spelled out by David Walsh in his blog. For our purposes, we also want to simulate the “raising” and “lowering” of the disc. This involves the CSS scale property, which we set in a javascript function. This is an example of pulling this to work together : on click of Flip, the system needs to stop Music, set the stylus to start position, stop spinning, raise itself a bit, move to the left a bit (to clear the spindle and tone arm), raise itself incrementally, THEN flip itself (trial and error to get proper transform-origin property since the Size of the disc when beginning the flip will not equal its resting size when animation is complete).   .. and then call a function that “prepares to play B.”

function to distinguish Playing song versus needle.

Another cool aspect is the “needle sound.” This was tricky because of a high level decision that to be accurate, it is the Player/Needle that makes the needle sound; rather than faking it by adding to the mp3 itself. Thus it is the lowering of the needle, and other instances where music is not playing but arm is on the disc, that generates the needle sound. What made this aspect complicated, and which effected the whole system, is Apple’s iOS not allowing more than one Audio object instance. Because the Side A, Side B, AND the needle sound have to share a audio object there must be quite a bit of tracking what should be playing.  Mostly this impacts the system’s “Listener” function which is called while the audio is playing.


So, I have to call it a day here in Blogville. You might want to know why I went to the trouble of making the virtual record player. I am starting a record company, and trying to figure out what that means these days. So I wanted an option to have a virtual player, but there are none at hand on the great Internet. This project also allowed me to work with the newest CSS 2-D and 3-D transformations, and to get back in the swing of web development after a summer break while I renovated a home.



Thank you jQuery, David Walsh Blog, MSDN, Source of Disc Image, Needle Sound.