Quickstart.

From an empty folder to an application running on your desk, registered as a device and deployed from GitHub. Fifteen minutes, no hardware required — the folder on your laptop is a device. All it asks of you is basic HTML and JavaScript.

Working

What you need

  • A computer — Linux, Windows or macOS — and a text editor.
  • Basic HTML and JavaScript. If you can make a web page with a button that does something, you know enough for everything on this page. No Linux, networking or device experience is needed.
  • A portal login (sign up) — to download ardaplayer in step 1, and again from step 5, when the folder becomes a device. Steps 2 to 4 run on your computer with no network at all.
  • A GitHub account, for step 6.

No device is needed: the folder on your computer acts as one until you have real hardware. If you already use a toolchain — Vite, esbuild, a Makefile — it works the same way: the player serves the finished files, not the tooling. A folder of hand-written files is just as good.

●
Worth installing first ArdaLive is our free VS Code extension for the authoring loop: live HTML preview that patches the DOM in place instead of reloading, so a multi-step kiosk screen keeps its state while you edit it. Independent of ArdaForm — it works on any plain HTML/CSS project.

1 · Get ardaplayer

ardaplayer is one program — the same one the devices run — with nothing else to install. Sign in to the portal, open Downloads, and pick the build for your computer: Linux desktop, Windows or macOS (the device builds and the OS images on the same page are for later). Put it somewhere your terminal can find it — a folder on your PATH. On Linux:

shell workstation
$ install -m 755 ardaplayer-linux-amd64-desktop ~/.local/bin/ardaplayer
$ ardaplayer -version
ardaplayer v1.2.3  linux/amd64

On Linux the desktop build opens the app in its own WebKit window, the same engine the devices draw with. The Windows and macOS builds open it in whatever Chrome-family browser is installed, or the system browser.

2 · Make a folder

An application is a folder with app/ inside it, and index.html at the root of app/. That is the whole contract — the player never writes into app/, and it is exactly what gets packed later. Make the two folders in a terminal as below, or in your file manager; then save this page as hello/app/index.html:

shell workstation
$ mkdir -p hello/app && cd hello
app/index.html
<!doctype html>
<html><body>
  <h1>Hello from <span id="who">…</span></h1>
  <p id="phase"></p>
  <script>
    // window.Arda exists before this line runs — no import, no wait
    Arda.device().then(function (d) {
      document.getElementById('who').textContent   = d.hostname
      document.getElementById('phase').textContent = 'phase ' + d.phase
    })
    // the device's state moved: registered, locked, resyncing — ask again
    Arda.on('device', function () { location.reload() })
  </script>
</body></html>

3 · Run it

In a terminal, inside the hello folder:

shell workstation
$ ardaplayer -dev .
ardaplayer v1.2.3 — ArdaForm device runtime
device local  — no control plane is dialled; the app runs on its own stores.
level=INFO msg="serving app" … dev=true

A window opens with your page in it. The phase reads local: nothing is connected and nothing is registered anywhere yet, so you can build with no network at all. The inspector is a right-click away; nothing reloads under your hands unless you say so. Say so in arda.dev.json, beside app/, not inside it:

arda.dev.json beside app/
{
  "window":  "1280x800",
  "console": true,
  "watch":   true,
  "can":     { "touch": true, "keyboard": false, "mouse": false }
}

can is what your app will be told the machine has, so a touchscreen layout can be built on a desk with a mouse — Arda.device() resolves with exactly this as its can. A flag on the command line always wins over the file, an unknown key is refused rather than ignored, and the file is never packed: outside developer mode the player refuses to start if it finds one.

4 · Data and settings

Most apps need some data — a member list, a price list — and a few settings somebody can change without touching the code — the kiosk's name, the currency. Both are declared in app/manifest.json: the data as datasets, the settings as fields an operator fills in. You decide what exists; the operator fills in the values in the portal.

app/manifest.json
{
  "datasets": [
    { "key": "members", "access": "ro", "api": "rw", "needForBoot": true },
    { "key": "visits",  "retention": { "days": 400 } }
  ],
  "settings": [
    { "key": "kiosk_name", "caption": "Kiosk name", "type": "text", "perDevice": true },
    { "key": "currency",   "caption": "Currency",   "type": "select",
      "options": ["GBP", "EUR"], "default": "GBP" }
  ]
}

members is filled through the HTTP API and read-only on devices; visits is what the kiosk writes. needForBoot says the app should not start until that dataset has arrived; everything else syncs behind it. Reading both from the page:

app/index.html excerpt
// A frozen object, readable on the first line. Absent is absent, so ?? is right.
var name = Arda.config.kiosk_name ?? 'Reception'

// Asking IS subscribing: the first call may answer null (nothing yet) and a 'sync' follows.
function draw() {
  Arda.db.dataset('members').then(function (rows) { render(Object.values(rows || {})) })
}
draw()
Arda.on('sync', function (dataset) { if (dataset === 'members') draw() })

// The only way up: a durable outbox, delivered at least once, survives power loss.
Arda.db.push('visits', { member: id, at: new Date().toISOString() })

Locally, the player builds a store for every dataset in the manifest, reads back what you push straight away, and loads seed/<key>.json beside app/ into an empty store — so seed/members.json with a few records gives you data to render before a server exists. Settings come from the manifest's defaults plus a "settings": {…} block in arda.dev.json.

5 · Turn the device on

One line in arda.dev.json makes the folder a real device: it registers with ArdaForm, receives its data and settings, and appears in the portal exactly as a till or a Pi would.

arda.dev.json
{ "window": "1280x800", "console": true, "watch": true, "device": true }
shell workstation
$ ardaplayer -dev .
ardaplayer v1.2.3 — ArdaForm device runtime
ACTIVATION CODE  K7X4P — enter in the portal: Devices → Add device

The window shows the same code. In the portal open the project, Devices, Add device, type it, and the phase moves to ready — settings arrive with it, a value changed in the portal later reaches the device in about three seconds, and your 'config' listener fires. Each app folder is its own device: its identity lives under hello/state/, so two folders are two rows in the portal with separate data. Keep state/, data/ and storage/ out of git; arda.dev.json may be committed because it is never packed. Records you pushed while local travel up the moment the device is on.

i
Real hardware instead? The deployment guide covers the Raspberry Pi and PC images, the USB installer, adopting a device from the portal's Adopt tab over the LAN or Bluetooth, and install codes that register a device with nobody typing anything.

6 · Deploy from GitHub

There is no build to upload. Devices run what a branch of your repository holds, packed and sealed by ArdaForm when it deploys — nothing from the repository is ever executed on the way.

  1. Push the folder to a GitHub repository (the app/ folder can sit at the root or in a subdirectory).
  2. Install the GitHub App at github.com/apps/ardaform on the account or organisation that owns the repository — until it is installed the portal can see nothing.
  3. In the project's Connectors tab press Connect GitHub, then add a connection with +: a project branch, the repository, the folder whose index.html is the app's root (here app), and the git branch to follow. A project branch reads one git ref; a device follows one project branch.
  4. Push a commit. ArdaForm reads the head, packs it, and every device on that branch is told within seconds.
shell workstation
$ git add -A && git commit -m "first kiosk screen" && git push
  …
  portal › Connectors:  main  reads main · a1f3c92 · 2 s ago · deployed

The Connectors tab shows the commit each branch has read and whether its devices run it; the Code tab shows each branch's latest push and its apply policy. The apply policy is set on the branch: as soon as updates arrive, only inside a time window in the device's own local time, or only when the app asks for them.

7 · Watch it run

Open the device from the Devices tab. Device view shows the screen on demand or every few seconds, the app's console as it happens, and evaluates an expression inside the running page. Alerts & log switches log capture on for a while and ships the last few hundred lines the device already kept, so you read what happened rather than waiting for it to happen again. The Screenshot button above the tabs captures every connected screen at full resolution as a lossless PNG.

Device view › evaluate
> Arda.config
  { kiosk_name: 'Front desk', currency: 'GBP' }
> Arda.db.status()
  { ready: true, datasets: { members: { synced: true, boot: true, records: 412 }, visits: {…} }, files: null }

8 · Handle updates

An update is downloaded, verified and staged; the app picks the moment. Say nothing and it applies five seconds later. Ask for time and you get sixty seconds, up to five times.

app/index.html excerpt
Arda.on('updateStart', function (u) {
  // u.version, u.mode, u.urgent, u.ask, u.ofAsks, u.decideInMs, u.pauseForMs
  if (!midCheckIn()) return              // silence: it applies in 5 s
  Arda.update.pause()                       // not yet — buys u.pauseForMs
  afterCheckIn(function () { Arda.update.apply() })
})

Applying is a page reload onto the new version. A restart applies whatever is staged with no negotiation — except on a branch set to apply only when the app asks — and an operator can apply from the device screen when the app's answer is wrong.

Where next

  • Developer manual — every area an app can reach, one page each, with checked examples; read the order of operations before turning a folder into a device.
  • Deployment guide — images, the USB stick, adopting devices, install codes, profiles, the device screen, player updates and decommissioning, with pictures.
  • Native APIs — the rest of window.Arda: the on-screen keyboard, screens and audio levels, cameras, printers, serial, Wi-Fi, webhooks.
  • Package format — what ArdaForm packs, what it refuses, and how a device verifies it.