Goal Seek in Google Sheets

Goal Seek for Sheets is an Add-On for Google Sheets for doing Goal Seek type data analysis.

In October 2019, Google launched an official Add-On, called “Goal Seek for Sheets”, and it is that Add-On that this tutorial references.

1. What is Goal Seek?

It’s a tremendously powerful and useful technique in data analysis. It’s a process where you set an output you want to achieve (e.g. break even, sell 10k units, save $1m) and let the computer find the input value that will get you there (e.g. 500 attendees, $100k capital lump sum, save $8k/year).

There are three components: 1) the unknown input variable, 2) the equation or calculation that is performed on the input variables to get the output, and 3) the known output.

The Goal Seek algorithm performs a series of “what-if” calculations by plugging in different input values. Each guess (hopefully) gets closer and closer to the solution.

For example, a classic use case of Goal Seek is to determine the number of sales required to break even, given other variables like fixed costs etc.

2. How do you use Goal Seek in Google Sheets?

Imagine Jennifer runs an annual conference for Google Sheet developers called “Sheet Freakz 🤟”.

She has a great venue picked out with room for 500 and she’s confident she can fill it. She knows what her costs are — the rental fee for the room, the cost of catering, the cost of promoting the conference — and she has agreed a $1,500 fee with 15 Google Sheets experts to come and talk about the latest and greatest in Sheets developments.

(Editor note: I wish this was a real conference!! 😄)

What price must she charge to cover her costs?

This is a classic break even cost analysis example that the Goal Seek Add-On is ideally suited for solving.

Setting up the Sheet

The first step is to simply add all of the known variables into a sheet, like so:

Goal Seek Variables in Google Sheet

These are the variables that Jennifer knows at the start of her problem.

Next, add a line for the registration fee per attendee, but set it to 0 for now. I’ve highlighted the cell yellow to indicate that it’s the solution cell that I want Goal Seek to solve for:

Goal Seek Variables

Finally, add a profit line, which is my revenue (# of attendees * registration fee) less expenses (fixed costs + (# of speakers * speaker fee)):

= ( B7 * B8 ) - ( B4 + ( B5 * B6 ) )

Goal Seek setup

Of course, initially, my profit is -$47,500 because I have no attendees and hence $0 revenue.

It’s time to use Goal Seek and let it find the break even registration fee for us.

How do you add Goal Seek in Google Sheets?

Goal Seek is an Add-On, which means you need to add it to your Google Sheet before you can use it.

Search for “Goal Seek” in the Add-Ons marketplace, found under the menu Add-ons > Get add-ons

The official Google Add-On information page will appear.

Click to install. That’s all it takes to add it to your Sheets.

Goal Seek for Sheets

You can also find it in the G Suite marketplace directly by clicking here.

Using Goal Seek

Open the Goal Seek sidebar: Add-ons > Goal Seek > Open

There are three pieces of information you need to enter:

i) Set Cell

Jennifer wants to know what price to charge to break even. In other words, what’s the minimum ticket price to ensure her profit is $0 and she doesn’t lose any money on the conference.

The “Set Cell” is the one we want to specify a value for. It’s the target we’re aiming for.

It’s the cell with the calculation formula in.

** With the Goal Seek sidebar open, click on the cell with the formula, which is cell B10 in this case (1). Add that reference to the Goal Seek solver by clicking on the grid icon next to the Set Cell box (2). This will auto-populate with the cell B10 reference. **

Select cells in Goal Seek for Sheets

ii) To Value

Next, type in the value of the output you want to achieve in the “Set Cell” box.

In this example, we want to set the profit value to 0, so we simply type 0 into this input box.

iii) By Changing Cell

What input variable do we want to vary to solve our equation?

In this case it’s the registration fee. It’s the variable that Jennifer is trying to find, such that her profit is $0 and she breaks even.

Select the cell that holds this variable and then click on the grid icon next to the input field to auto-populate it.

Read it out loud to understand what you’re asking the application to do: “Set Cell X To Value Y By Changing Cell Z”.

In our case, “Set Cell Profit To $0 By Changing Cell Registration Fee” or even cleaner “Set Profit To $0 By Changing Registration Fee”.

When you have all three inputs filled in for the Goal Seek application, the Solve button will turn blue and become active.

Press it.

The registration fee value will start jumping around all over the place as the computer tries different guesses to see what brings the profit value closer to 0.

Eventually it’ll find a solution and notify you that it’s done!

Goal Seek to determine break even cost

In this example, it’s found that the break even registration fee is $94.99999 dollars, or $95. Great!

Click here to open a Google Sheet template with all the examples from this tutorial >>

(Feel free to make your own copy: File > Make a copy…

If the file won’t open without permission, please open in an incognito window and copy from there.)

Manual Checks

It’s always a good idea to check the final solution that the Add-On finds, and not just trust it blindly.

In our example, it’s very simple to check that the two sides of the equation balance.

Jennifer’s expenses to run the conference are:

Expenses
$25,000 fixed costs + ( 15 speakers @ $1,500 each ) = 25,000 + ( 15 * 1,500 ) = $47,500

And her revenue on the other side of the equation will be:

Revenue
500 attendees * $94.99 registration fee (Goal Seek solution) = $47,499.99

The difference is simply a rounding error.

This is good.

The result from the Goal Seek is indeed a solution that gets Jennifer the break even registration price.

Another way to look at how the Goal Seek solver works is to visualize it. A very simplified version might look something like this:

Attempt 1

The computer has no idea what the solution is, so it makes a guess. For example, it might overestimate the result:

Guess 1: Overestimate

Attempt 2

The computer makes another guess. This time it might underestimate the result:

Guess 2: Underestimate

(It won’t always be a neat over/under/over/under guess. For example, if it guesses low, it might take many guesses before the first “over” guess happens. So this over/under flow is just to illustrate the concept.)

Attempt 3

With each additional “guess” the computer gets more accurate, because it uses the information from prior guesses to get closer to the solution. It might still overestimate, as shown here, but it’s getting closer to the solution:

Guess 3: Overestimate

Attempts 4 onwards

So on and so forth, as the computer makes guesses that get closer and closer, under, over, under, over, under, over, etc. until the solution is found:

Converging on solution

The program converges on the solution.

3. Other features of the Goal Seek Add-On

These features are all found in the sidebar underneath the input section for the Goal Seek variables.

Options

Under the Options menu, you can adjust the default settings for the Goal Seek solver.

You can change the 1) max number of iterations, 2) the tolerance (how accurate you need to be) and 3) the maximum time limit for the process, in seconds.

Goal Seek Settings

I’d suggest that the default settings will suffice for the majority of scenarios, but it’s good to know that you can make these changes should you need to.

Solve Status

The Solve Status box displays helpful information about the current Goal Seek solution.

It lets you know when the algorithm is finished, what the final status is, how many iterations it required and how long it took.

Solve Status in Goal Seek for Sheets

History

Access previous runs of the Goal Seek solver under the History menu.

Use the drop down menu to choose a prior solution based on a timestamp.

Goal Seek History

Error Messages

Occasionally the Goal Seek solver fails to find a solution.

One reason might be that the computer guesses get successively further away from the actual solution. They diverge away from the solution.

Should this happen, you’ll see an error message like this:

Goal Seek error message

4. More Goal Seek examples

Conference Break Even Example For Attendee Numbers

In the conference example above, instead of knowing how many people would attend, suppose Jennifer knew the registration fee.

She charges $299 for the registration fee and wants to know how many attendees she requires to break even?

The “changing cell” in this case becomes the number of attendees, rather than the registration fee.

The setup would be:

Goal Seek conference Example 2

The formula in cell B10 does not change from the original example. It’s still:

= ( B7 * B8 ) - ( B4 + ( B5 * B6 ))

The Goal Seek settings are:

Settings for conference example 2

Run Goal Seek and the answer comes back as 158.86.

In other words Jennifer needs 159 attendees paying a registration fee of $299 to break even.

Mortgage Calculation Example

Suppose you’re looking to buy a new house. You have an upper limit for your monthly payments of $1,500.

The other known variables in this case are: it’s a 30 year term with an annual interest rate of 4.5%.

What’s the maximum amount you can borrow?

Goal Seek can solve this for you.

Firstly, setup the sheet with the known variables in your Sheet:

Mortgage Goal Seek Example

The payment equation uses the PMT function in Google Sheets in cell B8:

= -PMT( B6 / 12 , B5 , B7 )

In Goal Seek, set the “Set Cell” to be this equation in cell B8.

Set the “To Value” to $1,500 (the maximum monthly payment that can be tolerated).

Set the “By Changing Cell” to the Amount Borrowed in cell B7 (currently $0.00).

Click Solve and let Goal Seek find your solution.

The algorithm will churn through the possible solutions until it settles on one that satisfies your tolerance (accuracy) setting.

In this case, the maximum amount you could borrow is $296,041.75.

Mortgage Solver Solution

Retirement Calculation Example

Suppose you want to retire with a pot of $1.5m in 40 years time. You’re confident of getting a 5% return on your investments.

What’s the annual contribution you need to make each year to hit this target?

Let’s use Goal Seek to find out.

Firstly, setup the sheet with the known variables in your Sheet:

Retirement Goal Seek Example

The calculation of the retirement pot value uses the Future Value function, the FV function, in Google Sheets in cell B7:

= FV( B5 , B4 , -B6 , 0 , 0 )

In Goal Seek, set the “Set Cell” to be this equation in cell B7.

Set the “To Value” to $1,500,000 (your target retirement pot).

Set the “By Changing Cell” to the Annual Contribution in cell B6 (currently $0.00).

Click Solve and let Goal Seek find your solution.

In this case, you need to contribute $12,417 each year to hit your retirement pot target of $1.5m.

Retirement Solution

Click here to open a Google Sheet template with all the examples from this tutorial >>

(Feel free to make your own copy: File > Make a copy…

If the file won’t open without permission, please open in an incognito window and copy from there.)

Resources

Google Documentation on the Goal Seek feature.

How to connect the Strava API with Google Sheets and Data Studio

This post looks at how to connect the Strava API with Google Sheets and create a visualization in Google Data Studio.

Strava api with google sheets and data studio

Strava is an insanely good app for athletes to track their workouts. I use it to track my running, biking and hiking, and look at the data over time.

This whole project was born out of a frustration with the Strava app.

Whilst it’s great for collecting data, it has some limitations in how it shows that data back to me. The training log shows all of my runs and bike rides, but nothing else. However, I do a lot of hiking too (especially when I’m nursing a running injury) and to me, it’s all one and the same. I want to see it all my activities on the same training log.

So that’s why I built this activity dashboard in Google Data Studio. It shows all of my activities, regardless of type, in a single view.

Connecting the Strava API with Google Sheets

To connect to the Strava API with Google Sheets, follow these steps:

Setup your Google Sheet

  1. Open a new Google Sheet (pro-tip: type sheet.new into your browser window!)
  2. Type a header row in your Google Sheet: “ID”, “Name”, “Type”, “Distance (m)” into cells A1, B1, C1 and D1 respectively
  3. Open the Script Editor (Tools > Script editor)
  4. Give the script project a name e.g. Strava Sheets Integration
  5. Create a second script file (File > New > Script file) and call it oauth.gs
  6. Add the OAuth 2.0 Apps Script library to your project (Resources > Libraries...)
  7. Enter this ID code in the “Add a library” box: 1B7FSrk5Zi6L1rSxxTDgDEUsPzlukDsi4KGuTMorsTQHhGBzBkMun4iDF
  8. Select the most recent version of the library from the drop-down (version 34 currently — September 2019) and hit “Save”

Add the code

If you’re new to API and Apps Script, check out my API Tutorial For Beginners With Google Sheets & Apps Script.

In your oauth.gs file, add this code:

var CLIENT_ID = '';
var CLIENT_SECRET = '';

// configure the service
function getStravaService() {
  return OAuth2.createService('Strava')
    .setAuthorizationBaseUrl('https://www.strava.com/oauth/authorize')
    .setTokenUrl('https://www.strava.com/oauth/token')
    .setClientId(CLIENT_ID)
    .setClientSecret(CLIENT_SECRET)
    .setCallbackFunction('authCallback')
    .setPropertyStore(PropertiesService.getUserProperties())
    .setScope('activity:read_all');
}

// handle the callback
function authCallback(request) {
  var stravaService = getStravaService();
  var isAuthorized = stravaService.handleCallback(request);
  if (isAuthorized) {
    return HtmlService.createHtmlOutput('Success! You can close this tab.');
  } else {
    return HtmlService.createHtmlOutput('Denied. You can close this tab');
  }
}

Also available in this GitHub oauth.js repo.

In your code.gs file, add this code:

// custom menu
function onOpen() {
  var ui = SpreadsheetApp.getUi();

  ui.createMenu('Strava App')
    .addItem('Get data', 'getStravaActivityData')
    .addToUi();
}

// Get athlete activity data
function getStravaActivityData() {
  
  // get the sheet
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getSheetByName('Sheet1');

  // call the Strava API to retrieve data
  var data = callStravaAPI();
  
  // empty array to hold activity data
  var stravaData = [];
    
  // loop over activity data and add to stravaData array for Sheet
  data.forEach(function(activity) {
    var arr = [];
    arr.push(
      activity.id,
      activity.name,
      activity.type,
      activity.distance
    );
    stravaData.push(arr);
  });
  
  // paste the values into the Sheet
  sheet.getRange(sheet.getLastRow() + 1, 1, stravaData.length, stravaData[0].length).setValues(stravaData);
}

// call the Strava API
function callStravaAPI() {
  
  // set up the service
  var service = getStravaService();
  
  if (service.hasAccess()) {
    Logger.log('App has access.');
    
    var endpoint = 'https://www.strava.com/api/v3/athlete/activities';
    var params = '?after=1546300800&per_page=200';

    var headers = {
      Authorization: 'Bearer ' + service.getAccessToken()
    };
    
    var options = {
      headers: headers,
      method : 'GET',
      muteHttpExceptions: true
    };
    
    var response = JSON.parse(UrlFetchApp.fetch(endpoint + params, options));
    
    return response;  
  }
  else {
    Logger.log("App has no access yet.");
    
    // open this url to gain authorization from github
    var authorizationUrl = service.getAuthorizationUrl();
    
    Logger.log("Open the following URL and re-run the script: %s",
        authorizationUrl);
  }
}

Also available in this GitHub code.js repo.

Note about the params variable

Have a look at the params variable:

var params = '?after=1546300800&per_page=200'

The ‘after’ parameter means my code will only return Strava activities after the date I give. The format of the date is epoch time and the date I’ve used here is 1/1/2019 i.e. I’m only returning activities from this year.

(Here’s a handy calculator to convert human dates to epoch timestamps.)

The other part of the params variable is the ‘per_page’ variable, which I’ve set to 200. This is the maximum number of records the API will return in one batch.

To get more than 200, you need to add in the ‘page’ parameter and set it to 2,3,4 etc. to get the remaining activities, e.g.

var params = '?after=1546300800&per_page=200&page=2'

Eventually you’ll want to do that programmatically with a while loop (keep looping while the API returns data and stop when it comes back empty handed).

Note about parsing the data

The script above parses the response from the API and adds 4 values to the array that goes into the Google Sheet, namely: ID, Name, Type and Distance.

You can easily add more fields however.

Look at the Strava documentation to see what fields are returned and select the ones you want. For example, you add total elevation gain like this:

activity.total_elevation_gain

If you add extra fields to the array, don’t forget to change the size of the range you’re pasting the data into in your Google Sheet.

The array and range dimensions must match.

Setup your Strava API application

You need to create your app on the Strava platform, so that your Google Sheet can connect to it.

Login to Strava and go to Settings > My API Application or type in https://www.strava.com/settings/api

This will take you to the API application settings page.

Give your application a name, and enter a website and description. You can put anything you want here, as it’s just for display.

The key to unlock the Strava API, which took me a lot of struggle to find, is to set the “Authorization Callback Domain” as

script.google.com

(Hat tip to this article from Elif T. Kuş, which was the only place I found this.)

Strava API application setup

Next, grab your client ID and paste it into the CLIENT_ID variable on line 1 of your Apps Script code in the oauth.gs file.

Similarly, grab your client secret and paste it into the CLIENT_SECRET variable on line 2 of your Apps Script code in the oauth.gs file.

Copy these two values:

Strava API application

And paste them into your code here:

Oauth client id and secret in Apps Script

Authorize your app

Run the onOpen function from the script editor and authorize the scopes the app needs (external service and spreadsheet app):

Apps Script authorization

If your process doesn’t look like this, and you see a Warning sign, then don’t worry. Click the Advanced option and authorize there instead (see how in this Google Apps Script: A Beginner’s Guide).

Return to your Google Sheet and you’ll see a new custom menu option “Strava App”.

Click on it and select the “Get data” drop-down.

Nothing will happen in your Google Sheet the first time it runs.

Return to the script editor and open the logs (View > Logs). You’ll see the authorization URL you need to copy and paste into a new tab of your browser.

This prompts you to authorize the Strava app:

Strava API with Google Sheets authorization workflow

Boom! Now you’ve authenticated your application.

For another OAuth example, have a look at the GitHub to Apps Script integration which shows these steps for another application.

Retrieve your Strava data!

Now, the pièce de résistance!

Run the “Get data” function again and this time, something beautiful will happen.

Rows and rows of Strava data will appear in your Google Sheet!

Connect Strava API with Google Sheets

The code connects to the athlete/activities endpoint to retrieve data about all your activities.

In its present setup, shown in the GIF above, the code parses the data returned by the API and pastes 4 values into your Google Sheet: ID, Name, Type and Distance.

(The distance is measure in meters.)

Of course, you can extract any or all of the fields returned by the API.

In the data studio dashboard I’ve used some of the time data to determine what day of the week and what week of the year the activity occurred on. I also looked at fields measuring how long the activity took.

Setting a trigger to call the API automatically

Once you’ve established the basic connection above, you’ll probably want to set up a trigger to call the API once a day to get fresh data.

You’ll want to filter out the old data to prevent ending up with duplicate entries. You can use a filter loop to compare the new data with the values you have in your spreadsheet and discard the ones you already have.

Building a dashboard in Google Data Studio

Google Data Studio is an amazing tool for creating visually stunning dashboards.

I was motivated to build my own training log that had all of my activities showing, regardless of type.

First, I created some calculated fields in Apps Script to work out the day of the week and the week number. I added these four fields to my code.gs file:

(new Date(activity.start_date_local)).getDay(), // sunday - saturday: 0 - 6
parseInt(Utilities.formatDate(new Date(activity.start_date_local), SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone(), "w")), // week number
(new Date(activity.start_date_local)).getMonth() + 1, // add 1 to make months 1 - 12
(new Date(activity.start_date_local)).getYear() // get year

And I converted the distance in metres into a distance in miles with this modification in my Apps Script code.gs file:

(activity.distance * 0.000621371).toFixed(2), // distance in miles

From there, you simply create a new dashboard in Data Studio and connect it to your Google Sheet.

The activity log chart is a bubble chart with day of the week on the x-axis and week number as the y-axis, both set to average (so each appears separately). The bubble size is the Distance and the dimension is set to Name.

Bubble chart in Data Studio

Next, I added a Year filter so that I can view each year separately (I’ve got data going back to 2014 in the dataset).

To complete the dashboard, I added a Strava logo and an orange theme.

Strava api with google sheets and data studio

(Note: There’s also an open source Strava API connector for Data Studio, so you could use that to create Strava visualizations and not have to write the code yourself.)

Next steps for the Strava API with Google Sheets

This whole project was conceived as a way to explore the Strava API with Google Sheets. I’m happy to get it working and share it here.

However, I’ll be the first to admit that this project is still a little rough around the edges.

But I am excited to have my Strava data in a Google Sheet now. There are TONS of other interesting stories / trends that I want to explore when I have the time.

There is definitely room for improvement with the Apps Script code. In addition to those mentioned above, and with a little more time, I would bake the OAuth connection into the UI of the Google Sheet front end (using a sidebar), instead of needing to grab the URL from the Logs in your script editor.

And the Data Studio dashboard was rather hastily thrown together to solve the issue of not seeing all my activity data in once place. Again, there’s a lot more work you could do here to improve it.

To be continued…

Time for a run first though!

Slicers in Google Sheets

Slicers in Google Sheets are a powerful new way to filter data in Pivot Tables.

They make it easy to change values in Pivot Tables and Charts with a single click. Slicers are extremely useful when building dashboards in Google Sheets.

Video: How Slicers Work And How To Add Them

What do Slicers in Google Sheets do?

Consider this basic dashboard in a Google Sheet. It consists of three small pivot tables and a chart, displaying (fictional) data about house sales.

Slicers in Google Sheets dashboard

You’ll notice the two black boxes above the chart labeled “Agent Test Slicer” and “Lead Source”. These are slicers in Google Sheets.

When you click on the drop-down arrow in the slicer, it brings up a filter menu:

Slicer filter menu

In the Home Sales dashboard example above, we can click on the slicers to focus on a subset of agents and/or subset of lead sources.

For example, we might select “Emma Johnson” only, so that we can see data that is just relevant to this person.

The three pivot tables (labeled 1, 2 and 3 in the image below) and the chart (labeled 4) are all updated to just show the rows of data from our dataset that are associated with “Emma Johnson”.

Slicer in Google Sheets

You’ll also notice that the slicer has updated the drop-down to say “1 of 4” instead of “All”, to signify that we’ve filtered on one value (“Emma Johnson”) from a possible set of 4 names.

(Note: you can absolutely choose more than one value at a time in your filter.)

How do you add a Google Sheets slicer?

(Note 8/1/19: According to the G Suite blog, it may take up to 15 days for feature visibility.)

Slicer Template

Feel free to make your own copy of this file (File > Make a copy…)

Slicer Template including the Home Sales Dashboard

The Data

Start with this table of fictitious real estate data (from Sheet1 of the template above):

Data in Google Sheets

Create a Pivot Table

If you’re new to Pivot Tables, have a read of Pivot Tables in Google Sheets: A Beginner’s Guide

Back in the real estate dataset, insert a Pivot Table: Data > Pivot Table

Create a simple Pivot Table in a new Sheet, for example this one shows property types and total sales price for each category:

Pivot Table

Add a Slicer Control

Back in the Data menu, choose: Data > Slicer

If your cursor was inside the Pivot Table when you added a Slicer, it’ll be added automatically and you’ll see the default slicer control:

Slicer column choice

If your cursor was outside the Pivot Table in another cell, you’ll be prompted to choose the data to use inside your slicer.

The first thing to do with a slicer is to select a column.

In this example, let’s choose the “Side” column, which is the column containing data about whether the agent acted as a Buyer or Seller in the transaction.

Our slicer will update to show the column name:

Slicer column added

We now have a perfectly good, working slicer.

However, you can customize it under the “Customize” menu and change the heading and formatting.

Here we give it a more descriptive title and change it to have a blue background:

Customize slicer in Google Sheets

Slicer Settings Menu

When you’ve finished setting up your slicer, you can get back to the editing menu by clicking the 3 dot menu next to the drop-down. This brings up the slicer’s settings menu:

Slicer Settings Menu

You can choose to copy the slicer (super helpful if you need to create several slicers), edit it, delete it or set the current filters as default.

Using Slicers

Clicking the drop-down arrow brings up the slicer filtering menu.

Slicer Filter in Google Sheets

This will look familiar if you use Filters with datasets in Google Sheets. It has exactly the same functionality, but now exists in a standalone control that you can position next to Pivot Tables and Charts as part of a dashboard report.

The data in our Pivot Tables will update when we change the filter applied in our Slicer menu.

Look at the data in the Pivot Table changing in this example as I change between Buyer and Seller:

Google Sheets slicer

I can’t wait to explore slicers in more depth and start adding them to more complex dashboards.

More information

Announcement: Additional tools for enhanced reporting in Google Sheets on the G Suite Updates Blog

Filter charts and tables with Slicers in the Docs Editors Help page.

New Course: Extreme Formulas in Google Sheets!

Extreme Formulas in Google Sheets

The newest premium training course from The Collins School Of Data, Extreme Formulas in Google Sheets, is now available.

It’s all about formulas, formulas, formulas!

The course is perfect for you if:

  • You enjoyed the Advanced Formulas 30 Day Challenge course
  • You’re intellectually curious and enjoy solving problems
  • You want to learn some crazy, cutting-edge formula techniques in Google Sheets
  • You enjoy the Formula Challenges in the Monday Tips emails
  • You’re an intermediate to advanced user of Google Sheets

For the next 72 hours, until Thursday 25th July at midnight, this course is available for just $49.

After that the price will increase to $99.

Get it now and enjoy a massive saving of 50% off the regular price!

Find out more details here >>

For a taste of what you’ll cover in this course, have a read of this post about the Google Sheets Formula Clock.

Google Sheets Formula Clock

Behold the Google Sheets Formula Clock, a working analog clock built with a single Google Sheets formula:

Google Sheets Formula Clock
Google Sheets Formula Clock sped up to show several hours

It’s a working analog clock built with a single Google Sheets formula.

That’s right, just a single formula. No Apps Script code. No widgets. No hidden add-ons.

Just a plain ol’ formula in Google Sheets!

Google Sheets Formula Clock

Google Sheets Formula Clock Template

Click here to open the Google Sheets Formula Clock Template

(Click to open the template. Feel free to create your own copy through the File menu: File > Make a copy...)

It might take a moment to update to the current time.

Part 1: Build your own Google Sheets Formula Clock

Step 1

Open a blank Google Sheet or create a new Google Sheet

(Pro-tip: type sheet.new into your browser address bar to do this instantly)

Step 2

Copy the Google Sheets Formula Clock formula below and paste it into the formula bar for cell A1 of your new Sheet:

=SPARKLINE(
ArrayFormula({
QUERY(ArrayFormula({
0, 0, 1 + N("See Comment 1");
0, 0, 0.8 + N("See Comment 2") ;
SEQUENCE(37,1,0,10),
SIN(RADIANS(SEQUENCE(37,1,0,10))),
COS(RADIANS(SEQUENCE(37,1,0,10))) + N("See Comment 3") ;
SEQUENCE(12,1,30,30),
0.9 * SIN(RADIANS(SEQUENCE(12,1,30,30))),
0.9 * COS(RADIANS(SEQUENCE(12,1,30,30))) + N("See Comment 4") ;
SEQUENCE(12,1,30,30),
SIN(RADIANS(SEQUENCE(12,1,30,30))),
COS(RADIANS(SEQUENCE(12,1,30,30))) + N("See Comment 5") ;
SEQUENCE(4,1,90,90),
0.8 * SIN(RADIANS(SEQUENCE(4,1,90,90))),
0.8 * COS(RADIANS(SEQUENCE(4,1,90,90))) + N("See Comment 6") ;
SEQUENCE(4,1,90,90),
SIN(RADIANS(SEQUENCE(4,1,90,90))),
COS(RADIANS(SEQUENCE(4,1,90,90))) + N("See Comment 7")
}),
"SELECT Col2, Col3 ORDER BY Col1",
0 + N("See Comment 8")
) ;
IF(
MINUTE(NOW()) = 0,
0,
SIN(RADIANS(SEQUENCE(MINUTE(NOW())/60*360,1,1,1)))
),
IF(
MINUTE(NOW())=0,
1,
COS(RADIANS(SEQUENCE(MINUTE(NOW())/60*360,1,1,1)))
) + N("See Comment 9");
0, 0 + N("See Comment 10") ;
0.75 * SIN(RADIANS((MOD(HOUR(NOW()),12)/12 * 360) + MINUTE(NOW())/60 * 30)),
0.75 * COS(RADIANS((MOD(HOUR(NOW()),12)/12 * 360) + MINUTE(NOW())/60 * 30)) + N("See Comment 11")
}),
{"linewidth",2 + N("See Comment 12")
+ N("
Comments:
1: Initial (0,1) coordinate at top of circle. Extra 0 included for sort.
2: Coordinates to create mark at 12 o'clock.
3: Coordinates to draw initial circle. Joins markers every 10 degrees starting from 0 at top of circle, e.g. 0, 10, 20, 30,...360
4: Sequence of coordinates every 30 degrees to create small markers for hours 1, 2, 4, 5, 7, 8, 10, 11
5: Sequence of coordinates to connect the 30 degree small markers. Needed to place them correctly on circle.
6: Sequence of coordinates every 90 degrees to create large markers for hours 12, 3, 6, 9
7: Sequence of coordinates to connect the 90 degree large markers. Needed to place them correctly on circle.
8: QUERY function used to sort the circle data by the degrees column, then select just the (x,y) coordinate columns (numbers 2 and 3) to use.
9: Coordinates to create the minute hand. Includes an IF statement to avoid an error when the minute hand arrives at the 12 mark.
10: Coordinates to return to centre of clock at (0,0) after minute hand, to be ready to draw hour hand.
11: Coordinates to create the hour hand.
12: Set linewidth of the Sparkline to 2.
.
.
Google Sheets Formula Clock
June 2019
Created by Ben Collins, Google Developer Expert and Founder of The Collins School Of Data
Website: benlcollins.com
Twitter: @benlcollins
"
)}
)

Initially it will look like this:

Paste Google Sheets Formula Clock in cell A1

Step 3

Make row 1 wider by hovering between rows 1 and 2 and using the grab hand to drag the row boundary down. Make the cell wide enough to create a circle:

Make the formula bar wider in Google Sheets

Step 4

This is the step that makes the clock tick!

Under File > Spreadsheet settings set the spreadsheet calculation settings to be “On change and every minute”, like so:

Google Sheet spreadsheet settings

This ensures that the NOW function is refreshed every minute, so our clock hands move around the circle. That’s it!

You should see the hands of your clock moving around the face.

Tick-tock! Tick-tock! 🕰️

Part 2: How Does It Work?

So there’s a few things going on here.

We need a way to get the current hour and minute values and have them update automatically.

Then somehow we need to draw a clock face with hands using…formulas? Huh? 🤔

Let’s run through the building blocks…

We can use our friend the SPARKLINE function to create the clock face:

Create A Circle With The Sparkline Function

The SPARKLINE function is used to create miniature charts inside a single cell. That’s its modus operandi.

However, we can also supply it with a range of x- and y-coordinates to create 2-d shapes, like a circle for example.

Use the following five steps to create a circle with a sparkline:

1) Start with this function in cell A1:

= SEQUENCE ( 37, 1, 0, 10 )

The Sequence function syntax is SEQUENCE(rows, columns, start, step) so in this example we’re creating 37 rows in a single column, starting from 0 and increasing in increments of 10 each.

I.e. it outputs a column of numbers representing every 10 degrees of a circle, up to 360 degrees.

2) In column B, we add this Array Formula in cell B1:

= ArrayFormula ( SIN ( RADIANS ( $A$1:$A$37 ) ) )

3) And in column C, this one in cell C1:

= ArrayFormula ( COS ( RADIANS ( $A$1:$A$37 ) ) )

Columns B and C now give you the coordinates of a circle.

4) Let’s plug them into the SPARKLINE function in cell D1 with this function:

= SPARKLINE ( B:C )

5) Lastly, make row 1 wider to show the circle.

Boom! 💥

The SPARKLINE function draws a circle for us:

Sparkline Circle in Google Sheets

Then, we need to create a time that automatically updates every minute. Thankfully that’s relatively easy to do with the NOW function:

NOW Function + Spreadsheet Settings

(Feel free to type these formulas in to the side of your sparkline workings in column B, C and D.)

= NOW()

The NOW Function in Google Sheets outputs a timestamp with a time to the nearest second. It’s a volatile function, which means it recalculates every time a change is made to the Sheet. In other words, it gives a new timestamp.

Per the Step 4 in Part 1 above, we can set the Sheet to update every minute, so the NOW function updates every minute.

Get The MINUTE And HOUR From NOW

It’s relatively easy to extract the minute and hour from the timestamp, with these two functions:

= MINUTE( NOW() )

and

= HOUR ( NOW() )

We need to convert these to degrees on a circle to show how far round the hands have gone.

The formulas become:

= MINUTE( NOW() ) / 60 * 360

and

= MOD( HOUR( NOW() ), 12 ) / 12 * 360

respectively.

Later we’ll need to convert these to RADIANS and then into coordinates for the sparkline function.

That’s the mechanics of the clock-tick-tock part, but we still need to add them to our sparkline clock.

Add The Clock Hands

The middle of our circle is represented by the coordinates (0,0).

Currently our sparkline has positioned us at the 12 o’clock position, represented by (0,1).

To add the minute hand, we need to draw another arc round the circle to travel around the edge of the circle to the current minute value, e.g. if it’s half past the hour then we need to draw another half circle to position ourselves at the bottom of the circle.

Then we can simply draw a line back to the centre of the circle, and that’s our minute hand!

So, add this function to cell B38:

=ArrayFormula( SIN ( RADIANS ( SEQUENCE ( MINUTE ( NOW( ) ) / 60 * 360 , 1 , 1 , 1 ) ) ) )

And add this one to cell C38:

=ArrayFormula( COS ( RADIANS ( SEQUENCE ( MINUTE ( NOW( ) ) / 60 * 360 , 1 , 1 , 1 ) ) ) )

Essentially, what these two formulas are doing is working out how many degrees around the circle we need to go, and calculating the coordinates.

Finally, let’s return to the centre of our circle, thereby drawing the minute hand.

In cell B398 put a 0.

In cell C398 put a 0.

They need to be on row 398 to give the array formula for the minutes enough space to expand (max 360 rows).

The “clock” now looks like this, and if you’ve set your spreadsheet to update every minute (see Step 4 in Part 1 above) then you’ll see this hand move around the clock.

Google Sheets sparkline minute hand

To add the hour hand, it’s a case of drawing a line from the centre coordinate (0,0) — where we are now — back out to the edge, again, going as far around the circle as needed to represent the current hour.

Add this formula to cell B399:

= 0.75 * SIN ( RADIANS ( ( MOD ( HOUR ( NOW( ) ) , 12 ) / 12 * 360 ) ) )

And this formula to cell C399:

= 0.75 * COS ( RADIANS ( ( MOD ( HOUR ( NOW( ) ) , 12 ) / 12 * 360 ) ) )

This adds the hour hand.

The 0.75 multiplier at the front of the formula shortens the hour hand a little to distinguish it from the second hand.

Boom!

Now you have a working clock:

Sparkline clock hour hand

Click here to view the template of this intermediary step.

Fix The Hour Issue

Unfortunately, in it’s current state, the formula breaks down at the top of the hour:

Google Sheets error message

This is easily solved by wrapping the minute hand calculation with an IF statement to set it to zero at the top of the hour. This IF statement tests to see if the minute component of NOW is equal to zero and sets the value to 0 if it is, otherwise we just proceed with the full SEQUENCE function.

Change the formula in cell B38 to

=ArrayFormula( IF( MINUTE( NOW() ) = 0 , 0 , SIN( RADIANS( SEQUENCE( MINUTE( NOW() ) / 60*360 , 1 , 1 , 1 )))))

and the formula in cell C38 to

=ArrayFormula( IF( MINUTE( NOW() ) = 0 , 0 , COS( RADIANS( SEQUENCE( MINUTE( NOW() ) / 60*360 , 1 , 1 , 1 )))))

It won’t look any different but you’ll avoid that error when the minute hand goes past the hour mark.

This formula is demonstrated in tab 2 of the intermediary template.

The clock will now look something like this:

Sparkline clock version 1

So what’s left?

Improvements

You might consider the following improvements, but I’ll leave these as a challenge for you:

  • Smoothing the hour hand, so it doesn’t jump in discrete steps from hour to hour but instead moves smoothly between the hours in proportion to the number of minutes passed. (See the GIF image at the start of this post.)
  • Adding tick marks at each of the 12 hour marks around the clock face.
  • Combining all the separate formulas into a single array formula. Hint: you need to make use of curly brackets { } to combine the array outputs from the constituent formulas.
  • Add comments to explain the parts of the formula (see adding comments using the N function)

Implementing all of these is a little tricky, not the least because the formula gets rather long!

The best approach is to build in steps, employing the Onion Method technique to avoid frustrating errors.

Hickory, dickory, dock.
The mouse ran up the sparkline clock.
The sparkline clock struck one,
The mouse ran down,
Hickory, dickory, dock. 🐁⏱️

What Else Can You Draw With Sparklines?

How about an outline of the Saturn V rocket?

Google Sheets sparkline Saturn V rocket

Or a pie chart built with a single sparkline array formula?

Google Sheets sparkline pie chart

This pie chart actually inspired the analog clock…you can probably see why!