This guide walks you through creating your first Alloy app for Pebble.
Platform Support: Alloy currently supports Emery (Pebble Time 2) and Gabbro (Pebble Round 2).
Use the pebble command-line tool to create a new Alloy project:
$ pebble new-project --alloy my-first-app
This creates a new directory with the following structure:
my-first-app/
src/
embeddedjs/
main.js # Watch app code (runs on Pebble)
pkjs/
index.js # Phone code (runs on connected phone)
resources/ # App resources (images, fonts, etc.)
package.json # App manifest and configuration
Alloy apps have two JavaScript environments:
| Location | File | Runs On | Purpose |
|---|---|---|---|
| embeddedjs | src/embeddedjs/main.js |
Watch | Your app UI and logic |
| PKJS | src/pkjs/index.js |
Phone | Network proxy, location, config |
The src/embeddedjs/main.js file is your watch app's entry point. This code
runs directly on the Pebble watch:
⌚ Watch (src/embeddedjs/main.js):
console.log("Hello, Pebble!");
The src/pkjs/index.js file runs on the connected phone. It's used for
network requests, GPS location, and configuration:
📱 PKJS (src/pkjs/index.js):
Pebble.addEventListener("ready", function(e) {
console.log("PebbleKit JS ready!");
});
The package.json file contains your app's metadata and configuration:
{
"name": "My First App",
"author": "Your Name",
"version": "1.0.0",
"keywords": ["pebble-app"],
"private": true,
"dependencies": {},
"pebble": {
"displayName": "My First App",
"uuid": "12345678-1234-1234-1234-123456789abc",
"projectType": "moddable",
"sdkVersion": "3",
"enableMultiJS": true,
"targetPlatforms": ["emery"],
"watchapp": {
"watchface": false
},
"messageKeys": [],
"resources": {
"media": []
}
}
}
Key fields in the pebble section:
| Field | Description |
|---|---|
displayName |
The name shown on the watch |
uuid |
Unique identifier for your app |
targetPlatforms |
Which Pebble platforms to build for |
watchapp.watchface |
Set to true for watchfaces, false for apps |
messageKeys |
Keys for watch-phone communication |
Alloy uses standard ECMAScript modules. You can split your code across multiple files and import them:
math.js
export function add(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
main.js
import { add, multiply } from "./math";
console.log("Sum: " + add(2, 3)); // 5
console.log("Product: " + multiply(4, 5)); // 20
Each additional module must be listed in src/embeddedjs/manifest.json so
the Moddable build system knows to compile it. The default manifest only
includes main.js:
"modules": {
"*": "./main"
}
To add more modules, change the value to an array of paths. The .js
extension is optional:
"modules": {
"*": [
"./main",
"./math"
]
}
If you import a module that isn't listed here, you'll get a module-not-found error at runtime.
Alloy runs on the XS JavaScript engine, which has some differences from browser or Node.js JavaScript:
All code runs in strict mode by default.
Built-in objects (primordials) are frozen and cannot be modified:
// This will throw an error:
Array.prototype.myMethod = function() {};
Evaluation of JavaScript source code by eval, Function and friends is not supported to keep minimize the code footprint of the JavaScript engine.
Since SDK 4.33, Alloy supports Explicit Resource Management:
using declarations, Symbol.dispose, DisposableStack and
SuppressedError. Alloy classes with a close() method (buttons, sensors,
messages, files, network clients and more) are disposable, so they can be
released automatically at the end of a scope:
function readScore(path) {
using file = device.files.openFile({ path });
const data = file.read(file.status().size, 0);
return JSON.parse(String.fromArrayBuffer(data)).score;
// file.close() runs automatically when the scope exits,
// even if read() or JSON.parse() throws
}
Use console.log() for debug output:
console.log("Debug message");
console.log("Value: " + someVariable);
Output appears in the Pebble emulator console or when viewing logs from a physical watch.
Note:
trace()is also available as a lower-level alternative, but requires a manual newline (\n) at the end of each message.console.log()is recommended for most use cases.
To build your app and run it in the emulator:
$ pebble build
$ pebble install --emulator emery
To install on a physical watch:
$ pebble install --phone YOUR_PHONE_IP
Now that you have a basic app running, explore the following guides: