Let's Build a Binary Clock in Node.js


I’m always on the lookout for fun programming exercises suitable for beginners. Recently, I came across this video on YouTube where the poster builds a binary clock in Javascript, HTML, and CSS. It was my first time coming across the idea of binary blocks and I thought it could be a fun exercise!

This is going to be my first tutorial-style post directed towards beginners so I’m still figuring out a proper format. One thing though, I’m not very fond of doing frontend and writing CSS so I decided to make this into a Node.js app. Another thing, I’m going to to use TypeScript for the code in this post. It’s JavaScript with some added features and it transpiles to JavaScript in the end. With that out of the way, let’s build a binary clock!

Pre-requisites

First, you need a quick tour over what the binary numbering system is. Here are some videos from Khan Academy:

Secondly, you need to know how a binary clock works. Here’s a great explanation on Reddit.

Finally, let’s install the required software:

  • Any text editor. I advice to start with VSCode
  • Node.js: a way to run JavaScript without a browser. I usually use NVM as a way to install multiple Node.js versions my machine (for Windows). After you install NVM, run nvm install --lts to install a long-term supported version of Node.js
  • Yarn: a package manager to install our dependencies

The Code

The full code is on GitHub. You can follow along with how the code progresses over time using the Git commits. I also advice to not copy the code and instead, type everything yourself. Keep this with you in your learning journey.

I. Setting up a new project

We will start first by creating a new project. In a terminal, we create a new folder using:

mkdir binary-clock

Then we move to that folder using:

cd binary-clock

Finally, we declare the folder as a JavaScript project using:

yarn init

You’ll be prompted to answer a few questions regarding the project and then you’ll find a file named pacakge.json in the current folder. This file contains the configurations and dependencies of your project.

Now, let’s setup TypeScript by running the following command:

yarn add -D typescript @types/node

You’ll find two changes in the current directory. The file package.json contains new lines under devDependencies and there’s a new folder named node-modules that holds the actual code of those dependencies.

Let’s add a new file named tsconfig.json which will hold our TypeScript configurations with the following content:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "lib": ["ESNext"],
    "moduleResolution": "node",
    "strict": true,
    "outDir": "dist",
    "rootDir": ".",
    "declaration": true,
    "allowSyntheticDefaultImports": true
  },
  "exclude": [
    "./node_modules",
    "./dist"
  ],
  "include": [
    "./src/**/*.ts"
  ]
}

Note: I don’t really know what all those options do. I have them copied from one project to the other. There’s an online reference for those.

The last step is to make building and running the project easier. Let’s add the following to package.json:

"scripts": {
  "compile": "tsc",
  "start": "node dist/src/index.js"
}

Now, we can create the src folder:

mkdir src

Inside src, we can create our main entry point file, index.ts (index is used by convention). The following command creates a new file under src directory:

touch src/index.ts

Add this to src/index.ts:

console.log("Hello, world!");

To build and run the project:

yarn compile && yarn start

II. Getting the current time

Our goal is to convert the current time into the binary format so we start by reading the current time. JavaScript provides a Date object that holds the current date and time. Since we only care about hours, minutes, and seconds, we use some methods defined on the Date object to extract this information:

// in src/index.ts

// read current date and time
const date = new Date();

// extract time information
let hours   = date.getHours();
let minutes = date.getMinutes();
let seconds = date.getSeconds();

Let’s wrap this in a function:

const _getTimeSections = (): Array<number> => {
  const date = new Date();

  let hours   = date.getHours();
  let minutes = date.getMinutes();
  let seconds = date.getSeconds();

  return [
    hours,
    minutes,
    seconds
  ];
}

Note: There are multiple ways to create functions in JavaScript. The above is called an arrow function.

The previous function takes nothing in its arguments and returns an array of elements of type number. That’s where TypeScript comes into play: the type system. JavaScript is a dynamic, weakly typed language. TypeScript, on the other hand, adds optional static typing to our programs. In fact, we can further annotate each variable with its type like so:

const _getTimeSections = (): Array<number> => {
  const date: Date = new Date();

  let hours: number   = date.getHours();
  let minutes: number = date.getMinutes();
  let seconds: number = date.getSeconds();

  return [
    hours,
    minutes,
    seconds
  ];
}

Now, since it’s declared/typed as a number, the compiler will complain if you tried to assign a string to the hours variable.

let hours: number = 12;
hours = "hello";  // ERROR!

Note: the name of the function starts with an _. This is a habit I picked from writing JavaScript since it does not have a notion of private functions, officially anyway. However, TypeScript does have a private keyword by default.

III. Converting the time to binary

In this section, we will do two tasks: extract each section (hours, minutes, and seconds) into two components (tens and ones) and convert each component to binary. Let’s start by extracting section components:

// in src/index.ts

const _getTimeSectionComponents = (section: number): Array<string> => {
  return section.toString().padStart(2, '0').split("");
}

Here we take a time section as a number, hours for example, and extract it into two components: tens and ones. For example, if it’s currently 21, we get an array of two elements: ['2', '1']. The call to padStart() function adds a zero to the right if the hour is only one digit. For example, 4 should be 04 and thus, we get: ['0', '4']. The split() function splits the string into an array. For example, '12'.split("") will return ['1', '2']. Notice that these functions work only on strings and that’s why we first called the toString() function on the section.

The second task is where we convert each component of each section into binary:

// in src/index.ts

const _convertComponentsToBinary = (components: Array<string>): Array<string> => {
  return components.map((component: string) => {
    return Number(component).toString(2).padStart(4, '0')
  });
}

The map() function is sort of like a for-loop except that it loops over some items/array and returns another array after applying a function on the given array. It sounded complicated writing this. Let’s take our example: given this ['2', '1'] as the components array, we use map to loop over each element of this array and apply the following logic on each one:

Number(component).toString(2).padStart(4, '0')

component is a variable that holds each element in the array. Let’s replace the variable with a value:

Number('2').toString(2).padStart(4, '0')

So Number() will convert the string element into a number, toString(2) will convert the number into the binary format as a string, and padStart(4, '0') will pad the result with up to 4 0’s (since each column in a binary clock has 4 rows). Applying all three functions on '2' will give us => '0010' (2 is 10 in binary).

The newly aquired value '0010' will be appended to an array and the iteration will move to the next value, '1'. The new iteration will apply the same three functions and return '0001'. This will be added to the array as well. Now that the iteration is done, the whole array, ['0010', '0001] will be returned from the map() function call and that’s our final return value.

VI. Printing the output

Here’s the function that prints the output to the terminal:

// in src/index.ts

const _printToConsole = (components: Array<Array<string>>) => {
  console.clear();

  for (let i = 0; i < 4; i++) {
    let row: string = "";
    for (let j = 0; j < 3; j++) {
      row += (components[j][0][i] + ' ');  // tens component
      row += (components[j][1][i] + ' ');  // ones component
    }

    console.log(row);
  }
}

It takes an array of components (each component is an array of strings [in binary format] in itself, ['0000', '0001']). The function starts first by clearing the terminal so we always have only the current time in the output. Then, it prints each row from each component. We have 4 rows, that’s the first loop. Then we have 3 sections * 2 components = 6 components in total, that’s the inner loop. We append the row elements into the row variable. Finally, we print each row after the inner loop.

V. Ticking the clock

Last but not least, let’s run our clock. We will use a JavaScript function named setInterval(). This function is used to execute some logic every defined number of milliseconds. It takes two arguments: a function containing the logic to execute, and the number of milliseconds that define the frequency. We use the functions we defined previously here, and pass 1000 milliseconds as the frequency to tick our clock every 1 second:

// in src/index.ts

setInterval(() => {
  let [hours, minutes, seconds] = _getTimeSections();

  let hourComponents   = _getTimeSectionComponents(hours);
  let minuteComponents = _getTimeSectionComponents(minutes);
  let secondComponents = _getTimeSectionComponents(seconds);

  let hourBinaryComponents   = _convertComponentsToBinary(hourComponents);
  let minuteBinaryComponents = _convertComponentsToBinary(minuteComponents);
  let secondBinaryComponents = _convertComponentsToBinary(secondComponents);

  let components = [
    hourBinaryComponents,
    minuteBinaryComponents,
    secondBinaryComponents,
  ];

  _printToConsole(components);
}, 1000);

VI. Extra: improvements

This is an extra part. You can take it as a homework, although, you’ll find the improvements in the final code on GitHub. A first improvement is to move the ticking logic inside setInterval() into a separate function. Another one is to wrap our functions inside a BinaryClock class and deal with it as an object. This is part of the object oriented programming capabilities provided by TypeScript. Here’s the documentation of classes and objects from TypeScript documentation page.


That’s the end of our tutorial. If you have any problems following along or any general comments, please do not hesitate to comment below or contact me.