SVG Full Year Gear Clock

This sketch displays an animated SVG-based mechanical gear clock that shows the current date and time. Embedded SVG gears rotate in synchronized patterns to represent seconds, minutes, hours, days, and months—creating a vintage clockwork visualization that updates in real-time.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make the seconds gear spin twice as fast
  2. Change the date header background color
  3. Speed up all gears to show 12 hours in 1 hour
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch embeds a beautiful mechanical gear clock as an SVG graphic that animates with the current time. The clock displays the full date in a yellow header, the current time in another header, and then uses a series of interlocking gears (drawn in SVG) that rotate at different speeds to visualize seconds, minutes, hours, days, and months. The gears are purely decorative—the real timekeeping happens in JavaScript by reading the system clock and calculating rotation angles.

The code is organized around a JavaScript Start() function that fires when the SVG loads, combined with a continuously updating timer that recalculates all gear rotations based on the current time. By studying it, you will learn how to embed SVG graphics in HTML, how to manipulate SVG element rotation using CSS transforms, how to extract date and time information from JavaScript Date objects, and how to synchronize multiple rotating elements to create the illusion of a working mechanical system.

⚙️ How It Works

  1. When the page loads, the HTML displays two yellow header sections—one showing today's date, one showing the current time—using standard JavaScript Date methods
  2. The SVG graphic loads and triggers the Start(evt) function, which initializes the animation system
  3. A setInterval timer fires every 50 milliseconds, calling a function that reads the current time and calculates rotation angles for each gear
  4. Each gear rotates at a rate proportional to its mechanical purpose: the seconds gear rotates 360 degrees every 60 seconds, the minutes gear rotates 360 degrees every 60 minutes, the hours gear rotates 360 degrees every 12 hours, and slower gears track days and months
  5. The rotation is applied to each SVG gear element using CSS transforms (rotate()), making it appear to spin smoothly
  6. The animation runs continuously, so as time passes, the gears rotate at their synchronized speeds, creating a working mechanical clock effect

🎓 Concepts You'll Learn

SVG embedding and manipulationCSS transforms and rotationJavaScript Date objectssetInterval for continuous animationTime-based rotation calculationsMechanical gear synchronizationReal-time clock systems

📝 Code Breakdown

Start(evt)

Start() is the entry point for SVG animation. It runs once when the SVG loads and then sets up a timer to continuously update the gears. This pattern—initialize once, then loop forever—is the backbone of real-time animation.

function Start(evt)
{
  Update();
  setInterval(Update, 50);
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

function-call Initial Update Update();

Draws the gears at their correct position for the current time before animation begins

function-call Continuous Timer setInterval(Update, 50);

Calls Update every 50 milliseconds to keep the gears rotating smoothly

function Start(evt)
This function runs automatically when the SVG finishes loading (triggered by onload='Start(evt)' in the HTML)
Update();
Immediately calls Update() once so the gears are positioned correctly at the moment the page loads
setInterval(Update, 50);
Schedules the Update function to run every 50 milliseconds (20 times per second), creating smooth continuous animation

Update()

Update() is the workhorse of this clock. It runs 20 times per second, reads the current time using JavaScript's Date object, calculates rotation angles by converting time units into degrees, applies those rotations to each SVG gear element, and updates the text headers with a formatted date and time string. Understanding how to convert time into angles is the key skill here—each time unit (seconds, minutes, hours, days, months) has its own multiplier (6, 6, 30, 15, 30) that converts it to degrees out of 360.

function Update()
{
  var now = new Date();
  var sec = now.getSeconds() + now.getMilliseconds() / 1000;
  var min = now.getMinutes() + sec / 60;
  var hour = now.getHours() % 12 + min / 60;
  var day = now.getDate() - 1 + hour / 24;
  var month = now.getMonth() + day / 31;

  var secgear = document.getElementById('secgear');
  secgear.setAttribute('transform', 'rotate(' + sec * 6 + ' 50 50)');

  var mingear = document.getElementById('mingear');
  mingear.setAttribute('transform', 'rotate(' + min * 6 + ' 50 50)');

  var hourgear = document.getElementById('hourgear');
  hourgear.setAttribute('transform', 'rotate(' + hour * 30 + ' 50 50)');

  var daygear = document.getElementById('daygear');
  daygear.setAttribute('transform', 'rotate(' + day * 15 + ' 50 50)');

  var monthgear = document.getElementById('monthgear');
  monthgear.setAttribute('transform', 'rotate(' + month * 30 + ' 50 50)');

  var date = now.getDate();
  var datestr = '';

  switch(now.getMonth())
  {
    case 0:
      datestr = 'January';
      break;
    case 1:
      datestr = 'February';
      break;
    case 2:
      datestr = 'March';
      break;
    case 3:
      datestr = 'April';
      break;
    case 4:
      datestr = 'May';
      break;
    case 5:
      datestr = 'June';
      break;
    case 6:
      datestr = 'July';
      break;
    case 7:
      datestr = 'August';
      break;
    case 8:
      datestr = 'September';
      break;
    case 9:
      datestr = 'October';
      break;
    case 10:
      datestr = 'November';
      break;
    case 11:
      datestr = 'December';
      break;
  }

  var day_name = '';
  switch(now.getDay())
  {
    case 0:
      day_name = 'Sunday';
      break;
    case 1:
      day_name = 'Monday';
      break;
    case 2:
      day_name = 'Tuesday';
      break;
    case 3:
      day_name = 'Wednesday';
      break;
    case 4:
      day_name = 'Thursday';
      break;
    case 5:
      day_name = 'Friday';
      break;
    case 6:
      day_name = 'Saturday';
      break;
  }

  var h = now.getHours();
  var m = now.getMinutes();
  var s = now.getSeconds();
  var ampm = 'AM';
  if(h >= 12)
  {
    ampm = 'PM';
  }
  if(h > 12)
  {
    h = h - 12;
  }
  if(h == 0)
  {
    h = 12;
  }
  var m_str = '';
  if(m < 10)
  {
    m_str = '0' + m;
  }
  else
  {
    m_str = '' + m;
  }
  var s_str = '';
  if(s < 10)
  {
    s_str = '0' + s;
  }
  else
  {
    s_str = '' + s;
  }

  document.getElementById('date').textContent = day_name + ' ' + datestr + ' ' + date;
  document.getElementById('time').textContent = h + ':' + m_str + ':' + s_str + ' ' + ampm;
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Time Extraction and Conversion var now = new Date(); var sec = now.getSeconds() + now.getMilliseconds() / 1000; var min = now.getMinutes() + sec / 60; var hour = now.getHours() % 12 + min / 60; var day = now.getDate() - 1 + hour / 24; var month = now.getMonth() + day / 31;

Reads the current time and converts each component (seconds, minutes, hours, days, months) into fractional values so gears can rotate smoothly between tick marks

for-loop Gear Rotation Application var secgear = document.getElementById('secgear'); secgear.setAttribute('transform', 'rotate(' + sec * 6 + ' 50 50)');

Finds the seconds gear element in the SVG and rotates it by applying a transform that spins it around its center point (50, 50)

switch-case Month Name Conversion switch(now.getMonth()) { case 0: datestr = 'January'; break;

Converts JavaScript's month number (0-11) into a human-readable month name

switch-case Weekday Name Conversion switch(now.getDay()) { case 0: day_name = 'Sunday'; break;

Converts JavaScript's day number (0-6) into a human-readable weekday name

conditional 12-Hour Clock Format if(h > 12) { h = h - 12; } if(h == 0) { h = 12; }

Converts 24-hour time (0-23) to 12-hour time (1-12) for display

conditional Zero-Padding Minutes and Seconds if(m < 10) { m_str = '0' + m; }

Adds a leading zero to single-digit minutes and seconds so they display as '09' instead of '9'

var now = new Date();
Creates a Date object containing the current date and time
var sec = now.getSeconds() + now.getMilliseconds() / 1000;
Gets seconds (0-59) and adds milliseconds converted to a fraction of a second, so the seconds gear rotates smoothly instead of ticking in jumps
var min = now.getMinutes() + sec / 60;
Gets minutes (0-59) and adds the fractional seconds, so the minutes gear gradually rotates as seconds accumulate
var hour = now.getHours() % 12 + min / 60;
Gets hours in 12-hour format (using modulo operator %) and adds the fractional minutes, so the hour gear moves smoothly
var day = now.getDate() - 1 + hour / 24;
Gets the day of the month (1-31), subtracts 1 to make it 0-30, and adds the fractional hours so the day gear rotates throughout the day
var month = now.getMonth() + day / 31;
Gets the month (0-11) and adds the fractional day, so the month gear rotates throughout the month
var secgear = document.getElementById('secgear');
Finds the SVG element with id 'secgear' (the seconds gear) in the HTML
secgear.setAttribute('transform', 'rotate(' + sec * 6 + ' 50 50)');
Rotates the seconds gear by sec * 6 degrees (since 360° / 60 seconds = 6° per second) around point (50, 50) which is its center
mingear.setAttribute('transform', 'rotate(' + min * 6 + ' 50 50)');
Rotates the minutes gear by min * 6 degrees around its center (360° / 60 minutes = 6° per minute)
hourgear.setAttribute('transform', 'rotate(' + hour * 30 + ' 50 50)');
Rotates the hours gear by hour * 30 degrees around its center (360° / 12 hours = 30° per hour)
daygear.setAttribute('transform', 'rotate(' + day * 15 + ' 50 50)');
Rotates the day gear by day * 15 degrees around its center (360° / 24 hours = 15° per hour, so it rotates completely each day)
monthgear.setAttribute('transform', 'rotate(' + month * 30 + ' 50 50)');
Rotates the month gear by month * 30 degrees around its center (360° / 12 months = 30° per month)
document.getElementById('date').textContent = day_name + ' ' + datestr + ' ' + date;
Updates the yellow date header with the formatted date string (e.g., 'Monday January 15')
document.getElementById('time').textContent = h + ':' + m_str + ':' + s_str + ' ' + ampm;
Updates the yellow time header with the formatted time string (e.g., '3:45:30 PM')

📦 Key Variables

now object

A JavaScript Date object containing the current date and time, accessed via methods like getSeconds(), getMinutes(), getHours(), etc.

var now = new Date();
sec number

The current seconds (0-59) plus milliseconds as a fraction, used to calculate the rotation angle of the seconds gear

var sec = now.getSeconds() + now.getMilliseconds() / 1000;
min number

The current minutes (0-59) plus fractional seconds, used to calculate the rotation angle of the minutes gear with smooth movement

var min = now.getMinutes() + sec / 60;
hour number

The current hours in 12-hour format plus fractional minutes, used to calculate the rotation angle of the hours gear

var hour = now.getHours() % 12 + min / 60;
day number

The current day of the month (0-30) plus fractional hours, used to calculate the rotation angle of the day gear

var day = now.getDate() - 1 + hour / 24;
month number

The current month (0-11) plus fractional days, used to calculate the rotation angle of the month gear

var month = now.getMonth() + day / 31;
datestr string

Stores the human-readable month name (e.g., 'January', 'February') converted from the month number

var datestr = 'January';
day_name string

Stores the human-readable weekday name (e.g., 'Monday', 'Sunday') converted from the day number

var day_name = 'Monday';
ampm string

Stores either 'AM' or 'PM' based on whether the hour is before or after noon

var ampm = 'AM';

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

PERFORMANCE Update() month and day name switches

Using long switch statements to convert month (0-11) and day (0-6) numbers to names is inefficient—these conversions happen 20 times per second even though the result rarely changes

💡 Create two constant arrays at the top of the file: `const monthNames = ['January', 'February', 'March', ...];` and `const dayNames = ['Sunday', 'Monday', ...];` Then replace the switch statements with simple array lookups: `datestr = monthNames[now.getMonth()];` and `day_name = dayNames[now.getDay()];` This reduces redundant calculations and improves readability.

STYLE Update() time formatting

The code manually pads minutes and seconds with leading zeros using conditional statements, which is verbose and error-prone

💡 Use JavaScript's built-in `padStart()` method: `const m_str = now.getMinutes().toString().padStart(2, '0');` This is shorter, clearer, and less likely to have bugs.

BUG Update() hour calculation

The 12-hour conversion logic is convoluted and could fail for edge cases—checking if h > 12 and then if h == 0 separately makes the code hard to follow

💡 Simplify with: `let displayHour = (h % 12) || 12;` This single line converts 0 to 12, leaves 1-11 unchanged, and converts 13-23 to 1-11, all with no ambiguity.

FEATURE SVG gear elements

The five gears all rotate around the same center point (50, 50), so they don't appear to mesh or interact mechanically

💡 Add different center points to each gear (stored as separate x,y coordinates in the rotation transform string) to position them around the clock face, creating the visual illusion that they are actually gears meshing together in a mechanical system.

🔄 Code Flow

Code flow showing start, update

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> update[Update] update --> initial-update[Initial Update] update --> timer-setup[Continuous Timer] update --> time-extraction[Time Extraction and Conversion] update --> gear-rotation[Gear Rotation Application] update --> month-name-switch[Month Name Conversion] update --> day-name-switch[Day Name Conversion] update --> 12-hour-conversion[12-Hour Clock Format] update --> zero-padding[Zero-Padding Minutes and Seconds] initial-update --> draw[Draw Gears at Current Time] timer-setup --> call-update[Call Update Every 50ms] time-extraction --> extract-time[Extract Current Time] time-extraction --> convert-time[Convert Time to Degrees] gear-rotation --> rotate-gears[Rotate Each Gear] month-name-switch --> convert-month[Convert Month Number to Name] day-name-switch --> convert-day[Convert Day Number to Name] 12-hour-conversion --> convert-24to12[Convert 24-Hour to 12-Hour] zero-padding --> pad-zero[Add Leading Zero if Needed] click update href "#fn-update" click initial-update href "#sub-initial-update" click timer-setup href "#sub-timer-setup" click time-extraction href "#sub-time-extraction" click gear-rotation href "#sub-gear-rotation" click month-name-switch href "#sub-month-name-switch" click day-name-switch href "#sub-day-name-switch" click 12-hour-conversion href "#sub-12-hour-conversion" click zero-padding href "#sub-zero-padding"

Preview

SVG Full Year Gear Clock - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of SVG Full Year Gear Clock - Code flow showing start, update
Code Flow Diagram