| name | pebble-watchface |
| description | Generate complete Pebble smartwatch watchfaces, build PBW artifacts, and test in QEMU emulator. Use when creating watchfaces, Pebble apps, animated displays, clock faces. Produces ready-to-install PBW files and runs them in emulator. |
Pebble Watchface Generator
Generate complete, buildable Pebble watchfaces with full PBW artifact output and QEMU testing.
CRITICAL: End-to-End Delivery
This skill MUST produce a final .pbw file, test it, AND visually verify it looks correct.
Every watchface request follows this complete flow:
- Research → [SUBAGENT] Gather requirements, study samples
- Design → [SUBAGENT] Plan architecture and visuals
- Implement → Write ALL project files
- Build → Run
pebble build to generate PBW
- Test → Run in QEMU, capture screenshot, visually verify with Read tool
- Iterate → Fix issues until screenshot looks good
- Generate Assets → Create app icons and preview GIFs using helper scripts
- Deliver → Report PBW location with verified screenshots, icons, and GIFs
Never stop until:
- PBW is built successfully
- Screenshots captured from QEMU for all platforms
- Visual verification confirms it looks correct
- App icons (80x80, 144x144) are generated
- Preview GIFs are created (for animated watchfaces)
- User receives the final artifacts
Phase 1: Research [USE SUBAGENT]
Spawn a research subagent using Task tool with subagent_type: "Explore" to:
Gather Requirements
Ask the user (use AskUserQuestion if unclear):
- Type: Digital, analog, animated, or artistic?
- Elements: Time, date, battery, weather, custom graphics?
- Animation: Static, subtle, or complex animations?
- Platforms: All platforms or specific?
Study Existing Code
The subagent should read and analyze:
samples/aqua-pbw/src/c/main.c
Key patterns to extract:
- Data structures for animated elements
- Animation loop structure
- Drawing functions
- Memory management patterns
- Battery-aware throttling
Also have subagent read relevant reference docs:
reference/pebble-api-reference.md
reference/animation-patterns.md
reference/drawing-guide.md
Subagent prompt example:
Research Pebble watchface patterns for an animated [description] watchface.
Read samples/aqua-pbw/src/c/main.c and extract:
1. How animations are structured
2. Drawing patterns for [specific elements]
3. Memory management approach
4. Relevant API calls from reference/pebble-api-reference.md
Return a summary of patterns to use.
Phase 2: Design [USE SUBAGENT]
Spawn a planning subagent using Task tool with subagent_type: "Plan" to:
Create Design Specification
- Screen layout (144x168 rectangular, 180x180 round)
- Element positions and sizes
- Animation behavior and timing (intervals, speeds)
- Color scheme (with B&W fallback for aplite)
- Data structures needed
- Layer hierarchy
CRITICAL: Layout Planning to Prevent Cropping
You MUST calculate exact pixel positions to ensure nothing is cropped.
For rectangular displays (144x168):
Available space: X: 0-143, Y: 0-167
Safe margins: 2-5 pixels from edges
For each visual element, calculate:
- Y position: Where does it start vertically?
- Element height: How tall is it?
- Bottom edge: Y_position + height must be < 168 (with margin)
Example layout calculation for a beach scene:
SCREEN_HEIGHT = 168
Time text: Y=5, height=45 → bottom at 50 ✓
Sky: Y=0, height=50 → bottom at 50 ✓
Ocean: Y=50, height=60 → bottom at 110 ✓
Sand: Y=110, height=58 → bottom at 168 ✓
Castle: Must fit within sand zone (Y=110-168)
- Castle height: 40px
- Castle Y: 168 - 40 - 5 = 123 (5px margin)
- Castle top: 123, bottom: 163 ✓
FAIL CONDITIONS to check in design:
- Element bottom edge >= SCREEN_HEIGHT
- Element right edge >= SCREEN_WIDTH
- GPath points with negative offsets that extend beyond anchor point
- Elements positioned relative to SCREEN_HEIGHT without accounting for element size
GPath Positioning Guide
GPaths use relative coordinates from an anchor point. Calculate carefully:
static GPoint castle_points[] = {
{-35, 0},
{-35, -40},
{35, 0},
};
gpath_move_to(castle_path, GPoint(SCREEN_WIDTH/2, 163));
GPath bounds check:
- Find min/max X in points array → gives width range from anchor
- Find min/max Y in points array → gives height range from anchor
- Calculate: anchor_x + min_x >= 0, anchor_x + max_x < SCREEN_WIDTH
- Calculate: anchor_y + min_y >= 0, anchor_y + max_y < SCREEN_HEIGHT
Architecture Planning
- What structs are needed?
- How many animated elements?
- Update interval (50ms smooth, 100ms battery-saving)
- Collision detection needed?
- Memory pre-allocation strategy
Subagent prompt example:
Plan the implementation for a Pebble watchface with [description].
Based on the research findings, create:
1. Data structure definitions
2. Layer hierarchy
3. Animation timing strategy
4. Drawing function list
5. File structure
Return a detailed implementation plan.
Phase 3: Implementation
Do this directly (not a subagent) - write all files:
Create Project Directory
mkdir -p /path/to/watchface-name/src/c
mkdir -p /path/to/watchface-name/resources
Write ALL Required Files
1. package.json (REQUIRED)
{
"name": "watchface-name",
"author": "Author Name",
"version": "1.0.0",
"keywords": ["pebble-app"],
"private": true,
"dependencies": {},
"pebble": {
"displayName": "Watchface Display Name",
"uuid": "GENERATE-NEW-UUID-HERE",
"sdkVersion": "3",
"enableMultiJS": false,
"targetPlatforms": ["aplite", "basalt", "chalk", "diorite"],
"watchapp": { "watchface": true },
"resources": { "media": [] }
}
}
Generate UUID: python3 -c "import uuid; print(uuid.uuid4())"
2. wscript (REQUIRED)
top = '.'
out = 'build'
def options(ctx):
ctx.load('pebble_sdk')
def configure(ctx):
ctx.load('pebble_sdk')
def build(ctx):
ctx.load('pebble_sdk')
binaries = []
for p in ctx.env.TARGET_PLATFORMS:
ctx.set_env(ctx.all_envs[p])
ctx.set_group(ctx.env.PLATFORM_NAME)
app_elf = '{}/pebble-app.elf'.format(ctx.env.BUILD_DIR)
ctx.pbl_program(source=ctx.path.ant_glob('src/c/**/*.c'), target=app_elf)
binaries.append({'platform': p, 'app_elf': app_elf})
ctx.set_group('bundle')
ctx.pbl_bundle(binaries=binaries, js=ctx.path.ant_glob(['src/js/**/*.js']))
3. src/c/main.c (REQUIRED)
Write complete watchface code following the design from Phase 2.
Use templates as starting points:
Code Requirements
#include <pebble.h>
- Implement
main(), init(), deinit()
- Window with load/unload handlers
tick_timer_service_subscribe() for time updates
- For animations:
app_timer_register() with 50ms interval
- Pre-allocate GPath in window_load
- Destroy all resources in unload handlers
- Fixed-point math only (sin_lookup/cos_lookup)
Phase 4: Build PBW
Run the Build
cd /path/to/watchface-name
pebble build
Verify Build Success
ls -la build/*.pbw
Expected output: build/watchface-name.pbw
Handle Build Errors
If build fails:
- Read error message
- Fix C code (syntax, types, missing includes)
- Run
pebble build again
- Repeat until successful
Phase 5: Test in QEMU Emulator
REQUIRED - Must test AND visually verify before delivering.
Step 1: Launch Emulator and Install
pebble install --emulator basalt
Wait a few seconds for the watchface to load and render.
Step 2: Capture Screenshot (MANDATORY)
pebble screenshot --emulator basalt screenshot_basalt.png
This saves the screenshot to the current directory.
Step 3: Visual Verification (MANDATORY)
Use the Read tool to view the screenshot image:
Read tool: screenshot_basalt.png
CRITICAL: Perform thorough visual verification using this detailed checklist.
A. Cropping Check (FAIL if any element is cut off)
B. Positioning Check (FAIL if layout doesn't match design)
C. Color Scheme Check (FAIL if colors don't match design)
D. Element Visibility Check (FAIL if key elements missing/invisible)
E. Design Intent Check (FAIL if doesn't match user request)
STOP AND FIX if ANY check fails. Do not proceed to delivery with visual issues.
Step 4: Fix Issues and Re-test
If visual verification fails, identify the issue category and apply the appropriate fix:
Fixing Cropping Issues
- Bottom cropping: Reduce Y coordinates of elements, or reduce element height
- Side cropping: Reduce X coordinates or width, ensure
SCREEN_WIDTH/2 centering
- Common mistake: Placing elements at Y positions that don't account for element height
- Fix formula: If element height is H and you want it at bottom, use
Y = SCREEN_HEIGHT - H - margin
Fixing Positioning Issues
- Wrong zone: Recalculate Y constants (e.g.,
SKY_HEIGHT, OCEAN_TOP, SAND_TOP)
- Off-center: Use
SCREEN_WIDTH / 2 - element_width / 2 for X position
- Overlapping: Ensure zone boundaries don't overlap (previous zone end < next zone start)
Fixing Color Issues
- Wrong color: Check
#ifdef PBL_COLOR blocks define correct GColor values
- B&W contrast: Use
GColorWhite/GColorBlack/GColorLightGray with high contrast
- Invisible element: Ensure element color differs from background color
Fixing Element Visibility Issues
- Element too small: Increase dimensions (radius, width, height, point coordinates)
- Element missing: Check if draw function is called, check NULL pointers
- Wrong z-order: Reorder draw calls (painter's algorithm: back to front)
Iteration Process:
- Identify which check(s) failed from Step 3
- Apply the specific fix from above
- Rebuild:
pebble build
- Reinstall:
pebble install --emulator basalt
- New screenshot:
pebble screenshot --emulator basalt screenshot_basalt.png
- Re-verify with Read tool using the FULL checklist
- Repeat until ALL checks pass
Do not skip any checks. A watchface with cropping issues is not acceptable for delivery.
Step 5: Test Other Platforms
After basalt looks good, test B&W and round:
pebble install --emulator aplite
pebble screenshot --emulator aplite screenshot_aplite.png
pebble install --emulator chalk
pebble screenshot --emulator chalk screenshot_chalk.png
Step 6: Check Logs for Errors
pebble logs --emulator basalt
Look for:
- APP_LOG errors
- Crashes or exceptions
- Memory warnings
Emulator Platforms Reference
| Platform | Display | Resolution | Priority |
|---|
| basalt | Color, Rect | 144x168 | Test first |
| aplite | B&W, Rect | 144x168 | Compatibility |
| chalk | Color, Round | 180x180 | Round layout |
| diorite | Color, Rect | 144x168 | Optional |
Phase 6: Deliver
Step 1: Generate App Icons and Preview GIFs
After all screenshots are captured, run the helper scripts to generate marketing assets:
python3 /path/to/skills/pebble-watchface/scripts/create_app_icons.py .
python3 /path/to/skills/pebble-watchface/scripts/create_preview_gif.py . --frames 8 --delay 400
This creates:
icon_80x80.png - Small app icon
icon_144x144.png - Large app icon
preview_basalt.gif - Animated color preview
preview_aplite.gif - Animated B&W preview
preview_chalk.gif - Animated round preview
Step 2: Report to User
After successful build AND visual verification:
-
PBW Location: build/watchface-name.pbw
-
Verified Screenshots: Show the user the captured screenshots
screenshot_basalt.png - Main color display
screenshot_aplite.png - B&W compatibility (if tested)
screenshot_chalk.png - Round display (if tested)
-
App Icons: Show the generated icons
icon_80x80.png - Small icon
icon_144x144.png - Large icon
-
Preview GIFs (if animated watchface):
preview_basalt.gif - Animated color preview
preview_aplite.gif - Animated B&W preview
preview_chalk.gif - Animated round preview
-
Visual Confirmation: Describe what the watchface shows:
- Time display style and position
- Animated elements (if any)
- Color scheme
- Any special features
-
Install Commands:
- Emulator:
pebble install --emulator basalt
- Device:
pebble install --cloudpebble
-
Summary: Brief description of what was built and verified
Subagent Summary
| Phase | Subagent Type | Purpose |
|---|
| Research | Explore | Read samples, extract patterns |
| Design | Plan | Create implementation plan |
| Implement | Direct | Write all project files |
| Build | Direct | Run pebble build |
| Test | Direct | Run in QEMU, screenshot, verify with Read tool |
| Iterate | Direct | Fix code until screenshot looks correct |
| Generate Assets | Direct | Run create_app_icons.py and create_preview_gif.py |
| Deliver | Direct | Report PBW + screenshots + icons + GIFs to user |
Quick Reference
Screen Dimensions
| Platform | Resolution | Shape | Color |
|---|
| aplite | 144x168 | Rect | B&W |
| basalt | 144x168 | Rect | 64-color |
| chalk | 180x180 | Round | 64-color |
| diorite | 144x168 | Rect | 64-color |
Key APIs
graphics_fill_circle(ctx, center, radius);
graphics_draw_line(ctx, start, end);
graphics_fill_rect(ctx, rect, corner_radius, corners);
sin_lookup(angle);
cos_lookup(angle);
time_t temp = time(NULL);
struct tm *tick_time = localtime(&temp);
tick_timer_service_subscribe(MINUTE_UNIT, tick_handler);
app_timer_register(50, callback, NULL);
layer_mark_dirty(layer);
Build & Test Commands
pebble build
pebble install --emulator basalt
pebble logs --emulator basalt
pebble screenshot --emulator basalt
pebble install --cloudpebble
Emulator Interaction Commands
Button Presses (pebble emu-button):
Press buttons on the emulator for testing interactive watchfaces/apps.
pebble emu-button click select --emulator basalt
pebble emu-button click back --duration 2000 --emulator basalt
pebble emu-button click down --repeat 5 --emulator basalt
pebble emu-button push up --emulator basalt
pebble emu-button release --emulator basalt
Options:
--duration / -d - Duration in ms for click (default: 100)
--repeat / -n - Number of times to repeat (default: 1)
--interval / -i - Interval in ms between repeats (default: 200)
Buttons: back, up, select, down
NOTE: On watchfaces, select opens the launcher and up/down open timeline. Use accel_tap_service_subscribe() for shake-to-interact instead of buttons.
Accelerometer Tap (pebble emu-tap):
Simulate shake/tap gestures for watchfaces that use accelerometer input.
pebble emu-tap --emulator basalt
for i in {1..13}; do pebble emu-tap --emulator basalt; sleep 0.3; done
Constraints
- No Floating Point - Use sin_lookup/cos_lookup only
- Pre-allocate Memory - Create GPath in window_load
- Battery Aware - Throttle when battery < 20%
- Clean Resources - Destroy in unload handlers
- NULL Checks - Verify pointers before use
- Overflow Protection - Use modulo on counters
File Checklist
Before building:
Build: pebble build
Test: pebble install --emulator basalt
Output: build/[name].pbw