The other day I saw a pretty cool Garmin Watch Face on Reddit, and before I could download it, I realized it was a paid face :/ I very much dislike paid apps, so this was the trigger for me to embark on the journey of creating my own Watch Face, all because of 2.99$.
Garmin SDK – Developer Account
Let’s go – first thing is a visit to Garmin’s developer page which is the first time i heard of it 🙂
https://developer.garmin.com/connect-iq/sdk – Download Connect IQ SDK
During installation you can use your own Garmin account to login – there is no need to create a special developer account. Then pick the desired SDK version and everything will be downloaded (including the API support for all devices)
Then we need to install the extension Monkey C in Visual Studio

After installing, and restarting Visual Studio we need to command+shit+P (on a Mac) Type “Verify Installation” and select Monkey C: Verify Installation – then choose generate developer key
Sample Project
I guess the best way to get started is with a sample project, and we can easily get that by doing command+shit+P (on a Mac) and then Monkey: New Project

Then we can also pick all the target products for the Watch Face.

At this point there is a sample project that we can work with. I also decided to download a couple of publicly available github repos like Crystal Face and Yet Another Watch Face, which provide already a proper implementation which will be good for a better understanding of what will be required.
The Big Challenge
All of this is pretty fun until the moment one realizes that this is all coded in Monkey which looks a bit like a mix of JavaScript, Rust, and Go – and I really don’t have the energy to spend time learning any of this at this moment.
CODEX App Enters The Room
But we live in the Claude and CODEX as the perfect assistant co-coders, and I am using the co prefix very loosely here because clearly they will be the main coders here 🙂
The Plan
I won’t attempt to re-create the original WatchFace that triggered this exercise, which seems quite complex to start with. This was the moment where I decided to create a Face with the FCPorto theme, which is something it does not exist in the store.
- Simple layout
- Time + core metrics
- FCPorto Dragon as a logo
Project Structure
There are a couple artifacts worth while understanding in this project.
At the root directory, the manifest.xml file holds the configuration of the application.
<?xml version="1.0"?>
<!-- This is a generated file. It is highly recommended that you DO NOT edit this file. -->
<iq:manifest version="3" xmlns:iq="http://www.garmin.com/xml/connectiq">
<iq:application id="xxxxxxxxxx"
type="watchface"
name="@Strings.AppName"
entry="FCPWatchFaceApp"
launcherIcon="@Drawables.LauncherIcon"
minApiLevel="2.3.0">
<iq:products>
<iq:product id="epix2"/>
<iq:product id="fenix7"/>
<iq:product id="fr955"/>
<iq:product id="fr965"/>
</iq:products>
<iq:permissions>
<iq:uses-permission id="Background"/>
<iq:uses-permission id="Communications"/>
<iq:uses-permission id="Positioning"/>
</iq:permissions>
<iq:languages>
<iq:language>ces</iq:language>
<iq:language>dan</iq:language>
<iq:language>deu</iq:language>
<iq:language>dut</iq:language>
<iq:language>eng</iq:language>
</iq:languages>
<iq:barrels/>
</iq:application>
</iq:manifest>
This file defines the app identity, watch model support, permissions, languages, and API compatibility level.
Then you get the resources folder that contains assets and configuration files such as images, drawables, strings, layouts, and settings.
In properties.xml, you define the default values for watch face customization options:
<properties xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://developer.garmin.com/downloads/connect-iq/resources.xsd">
<property id="BackgroundColor" type="number">0x000000</property>
<property id="BackgroundMode" type="number">0</property>
<property id="IconPalette" type="number">0</property>
<property id="FontFamily" type="number">1</property>
<property id="MetricSlot1" type="number">0</property>
<property id="MetricSlot2" type="number">1</property>
<property id="MetricSlot3" type="number">2</property>
<property id="MetricSlot4" type="number">4</property>
<property id="AlwaysOnMinimal" type="boolean">false</property>
<property id="UseMilitaryFormat" type="boolean">true</property>
<property id="ShowDebugGrid" type="boolean">false</property>
</properties>
These properties provide defaults for theme, fonts, metric placement, time format, and debug behavior.
In drawables.xml, drawable IDs are mapped to image files:
<drawables xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://developer.garmin.com/downloads/connect-iq/resources.xsd">
<bitmap id="LauncherIcon" filename="launcher_icon.svg" dithering="none" />
<bitmap id="BlueBackground" filename="background3_545.png" />
<bitmap id="PortoLogo" filename="dragao_110pct_iconblue.png" />
<bitmap id="PortoLogoRubikDirt" filename="dragao_rubik_dirt_145_iconblue.png" />
</drawables>
Then you find the source folder for the classes written in Monkey C:
- FCPWatchFaceApp.mc: app entry point and view initialization.
- FCPWatchFaceView.mc: main watch face rendering logic (onUpdate, drawing, data fields).
- FCPWatchFaceBackground.mc: background-specific rendering responsibilities.
This separation keeps the project clean: app lifecycle in one file, UI updates in another, and background rendering in its own module.
The Struggle
The hardest part, by far, has been adjusting the layout. Everything is coordinates based, which makes it a massive pain to get right, even with Codex. Getting the font and icon sizes right has been just as tricky. I can see how a background in UX would make this much smoother, but coming from a drag-and-drop world (hello OIC and ODA), this pixel-perfect approach feels completely alien.
After some back and forward with my side kick Codex, I was able to get a satisfactory results – at least for now.

Now lets bring this to my watch.
Build & Install
When the application is built, the binary .prg file is generated (in this project, under build/)
In order to install the watch face on your watch:
- Connect the watch to your computer using the Garmin USB cable.
- Open \GARMIN\APPS.
- Copy the .prg file
- Safely eject the device
The new watch face will now appear in the watch face selection settings on the watch.
None of the above instructions worked for me because at this moment I only have my corporate laptop which has restrictions on external drives. So I need to go via a different route – the one of publishing the App in Garmin Store.
Submit An App
Garmin gives us the possibility to upload an app and mark it as beta, for developing/testing purposes.
https://developer.garmin.com/connect-iq/submit-an-app

I followed this process, and after a couple details added all was ready – The part that actually took the longest was getting the app screenshots to match Garmin’s exact resolution and size requirements

At this point the App is visible in Connect IQ, but only for my Developer account. I must say I found it hard to find the app. I only managed to install it after opening the app’s page link on my phone, which then redirected me to the correct place inside the Connect IQ app. From there, installation finally worked.
The Result

As of today I submitted the version 1.0.0 of the App to the Garmin Store, but it’s under approval so it may take some days before it becomes public.
Leave a Reply