Automate Your Google Calendar Coloring

Mathias Wagner
6 min readDec 13, 2023

Tl;dr: Using colors to visually cluster your calendar events is a great productivity hack. I’ll briefly explain my personal system of adding context to a calendar developed during my time at Google. Then I will give you a little piece of code, to automate the colorization of your Google Workspace calendar.

From blue to context with color: what we want to achieve.

How I use colors in my calendar

I started using colors in my calendar already in Outlook, quite some time ago. In Google calendar it basically works the same way, with an important addition (not sure if Outlook has a similar view nowadays). The time insights feature does not only tell you with whom you meet most often or if you do more 1:1s vs. multi-person meetings. It also tells you how much time you spend per week based on event colors.

Knowing that I spent 10.3h on travel a week and have been with customers 8.5h can be valuable for my time management and prioritization. I use nine labels with a dedicated color to organize myself.

There are many ways of organizing your calendar. Mine is just one, which should inspire you to develop yours.

You can label the available 10 colors (+1 default color) in any way you find useful. For me the labels are:

  • Client — for any meeting with a customer or partner
  • Internal — for meetings with internal colleagues only, which are not matching a label below (e.g. Event)
  • Travel — for time I spend on trains or planes
  • Blocker — for slots I am not available (e.g. a dentist appointment)
  • OoO — for absences (mine or colleagues)
  • Training — for well… trainings
  • Interview — for anything recruiting related
  • Event — for all hands, business dinners or a party
  • ToDo — for things I need to get done requiring time

Using Google App Script to automate colorizing your calendar

Now to the fun part 😀. In my previous job at Google somebody wrote an internal browser extension to set event colors based on predefined event titles and also allowed some Regex shenanigans. As I couldn’t find anything useful or trustworthy in the public Chrome extension store, I did some coding on my own. This replicates all the functionality — on Google App Script level, not as a browser extension — and adds detection of external participants on top. Yes, I know that there is no error or exception handling. It just works ;).

My coding skills and time do not allow me to make a fully fledged extension out of this. But if you know how to create a Google App Script and schedule automatic execution, you are good to go. I give some hints, too.

Deployment and Automation

Google App Script makes it easy to create and deploy a script. Predefined triggers, like “calendar update” do exist and are perfect for our use case. Time bound triggering is also possible, but would be a waste of compute resources most of the time. Also, a user would expect a color change in a matter of seconds after creating or getting a new invite instead of e.g. once per hour.

This is a very brief overview of how to make it happen. If you struggle, please refer to the official documentation. Please find a screenshot for each of the below 4 steps at the very end of this article.

  1. Copy the below code or use my shared file
  2. Put it in a new Google App Script project
  3. Make adjustments as needed 😀
  4. Deploy it and assign a trigger

The Code

I commented on the code. So, read it if you are interested in how it works 😀.

/* Written by Mathias Wagner www.linkedin.com/in/mathias-wagner */


// Global debug setting
// true: print info for every event
// false: print info for only newly colorized event
const DEBUG = false


/* Entry for the whole colorizing magic.
Select this function when deploying it and assigning a trigger function
*/
function colorizeCalendar() {

const pastDays = 1 // looking 1 day back to catch last minute changes
const futureDays = 7 * 4 // looking 4 weeks into the future

const now = new Date()
const startDate = new Date(now.setDate(now.getDate() - pastDays))
const endDate = new Date(now.setDate(now.getDate() + futureDays))
// Extracting the domain of your email, e.g. company.com
const myOrg = CalendarApp.getDefaultCalendar().getName().split("@")[1];

// Get all calender events within the defined range
// For now only from the default calendar
var calendarEvents = CalendarApp.getDefaultCalendar().getEvents(startDate, endDate)


if (DEBUG) {
console.log("Calendar default org: " + myOrg)
}


// Walk through all events, check and colorize
for (var i=0; i<calendarEvents.length; i++) {


// Skip for better performance, else go to colorizing below
if (skipCheck(calendarEvents[i])) {
continue
}
colorizeByRegex(calendarEvents[i], myOrg)
}
}


/* Performance tweak: skip all events, that do no longer have the DEFAULT color,
or have been declined already.
This avoids overriding user settings and doesn't burn regex / string ops
for allready adjusted event colors.


@param CalendarEvent
*/
function skipCheck(event) {


if(event.getColor() != "" || event.getMyStatus() == CalendarApp.GuestStatus.NO) {
if(DEBUG) {
console.log("Skipping already colored / declined event:" + event.getTitle())
}
return true
}
return false
}


/* Actual colorizing of events based on Regex matching.
Makes only sense for frequent stuff you want to auto colorize.
Order matters for performance! Function exits after first matching color set.

https://developers.google.com/apps-script/reference/calendar/event-color
Mapping of Google Calendar color names to API color names (Kudos to Jason!):
https://lukeboyle.com/blog/posts/google-calendar-api-color-id
@param CalendarEvent
@param String
*/
function colorizeByRegex(event, myOrg) {


// Converting to lower case for easier matching.
// Keep lower case in mind when defining your regex(s) below!
eventTitle = event.getTitle().toLowerCase()

// Check for "blockers" in my calendar
if (/dnb/.test(eventTitle) ||
/blocker/.test(eventTitle)) {

console.log("Colorizing DNB found: " + eventTitle)
event.setColor(CalendarApp.EventColor.YELLOW)
return
}


// Check for travel related entries
if(/journey from/.test(eventTitle) ||
/stay at/.test(eventTitle ||
/flight to/.test(eventTitle))) {


console.log("Colorizing travel found: " + eventTitle)
event.setColor(CalendarApp.EventColor.GREEN)
return
}


/* Check external participation.
NB: If there are no external participants, one could mark this
with the "INTERNAL" color. But that would also colorize
e.g. trainings or events as "INTERNAL", which is technically
correct, but you might want to give those a "special" color.
*/
if (checkForExternal(event, myOrg)) {

console.log("Colorizing external event found: " + eventTitle)
event.setColor(CalendarApp.EventColor.RED)
return
}


// Check for my team name to mark internal meetings
// or myself for 1:1s
if (/my team name/.test(eventTitle) ||
/shortname/.test(eventTitle) ||
/mathias/.test(eventTitle)) {


console.log("Colorizing internal stuff found: " + eventTitle)
event.setColor(CalendarApp.EventColor.BLUE)
return
}


// Check for interviews
if (/interview/.test(eventTitle)) {


console.log("Colorizing interview stuff found: " + eventTitle)
event.setColor(CalendarApp.EventColor.PALE_RED)
return
}


// Check for training related meetings
if (/training/.test(eventTitle) ||
/class/.test(eventTitle) ||
/'some thing' release demo/.test(eventTitle)) {

console.log("Colorizing training found: " + eventTitle)
event.setColor(CalendarApp.EventColor.ORANGE)
return
}


// No match found, therefore no colorizing
else {
console.log("No matching rule for: " + eventTitle)
}
}


/* Check participants for external domain other than myOrg. Requires adjustment
if you have multiple "internal" domains, like company.com and company.de.
The first external participant match exits this function.
@param CalendarEvent
@param String
*/
function checkForExternal(event, myOrg) {

// Create guest list including organizer -> true parameter
const guestList = event.getGuestList(true)

// Building guest email and domain arrays, not needed by me.
// Uncomment if you need it
/*
var guestEmails = []
var guestDomains = []


for (guest of guestList) {
guestEmails.push(guest.getEmail())
guestDomains.push(guest.getEmail().split("@")[1])
if (DEBUG)
console.log("Participant emails are: " + guestEmails)
}
*/


for (guest of guestList) {


// get domain of guest and match to my domain
if (guest.getEmail().split("@")[1] != myOrg) {

console.log("External domain found: " +
guest.getEmail().split("@")[1] + " in " + event.getTitle())
return true
}
}
return false
}

Screenshots for creating and deploying above script

  1. Copy the below code or use my shared file

2. Put it in a new Google App Script project

3. Make adjustments as needed 😀

4. Deploy it and assign a trigger

--

--