This guide walks you through your first Andromeda programs and the major APIs.
Your First Program
Create hello.ts:
console.log("Hello, Andromeda!");
console.log("Current time:", new Date().toISOString());Run it:
andromeda run hello.tsOutput:
Hello, Andromeda!
Current time: 2026-05-11T15:30:45.123ZShorthand:
andromeda hello.tsis equivalent toandromeda run hello.tswhen the first argument is a.tsfile.
Working with Files
Andromeda exposes both sync and async file APIs on the global Andromeda
object.
const notes = `# My Notes
- Learn Andromeda
- Build something cool
- Share with friends
`;
// Sync API
Andromeda.writeTextFileSync("notes.md", notes);
console.log(Andromeda.readTextFileSync("notes.md"));
// Async API
await Andromeda.writeTextFile("notes.copy.md", notes);
const copy = await Andromeda.readTextFile("notes.copy.md");
console.log(copy);
// Binary files
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
Andromeda.writeFileSync("hello.bin", bytes);
const back = Andromeda.readFileSync("hello.bin");
console.log(back.length);
// Environment variables and CLI args
const home = Andromeda.env.get("HOME") || Andromeda.env.get("USERPROFILE");
console.log("Home directory:", home);
console.log("CLI args:", Andromeda.args);Creating Graphics with Canvas
Andromeda ships a GPU-accelerated 2D Canvas API via OffscreenCanvas:
const canvas = new OffscreenCanvas(400, 300);
const ctx = canvas.getContext("2d")!;
ctx.fillStyle = "#1a1a1a";
ctx.fillRect(0, 0, 400, 300);
const colors = ["#ff6b6b", "#4ecdc4", "#45b7d1", "#96ceb4", "#feca57"];
for (let i = 0; i < colors.length; i++) {
ctx.fillStyle = colors[i];
ctx.fillRect(50 + i * 60, 100, 50, 100);
}
ctx.fillStyle = "#ffffff";
ctx.font = "24px sans-serif";
ctx.fillText("Hello, Andromeda!", 100, 50);
// Save to disk
canvas.saveAsPng("my-first-graphic.png");
// Or get raw bytes / a data URL / a Blob
const png = canvas.toBuffer("image/png");
const dataUrl = canvas.toDataURL("image/png");
const blob = await canvas.convertToBlob({ type: "image/png" });
console.log("PNG bytes:", png.length, "data URL prefix:", dataUrl.slice(0, 30));Cryptography
// Random UUID
console.log(crypto.randomUUID());
// Secure random bytes
const bytes = crypto.getRandomValues(new Uint8Array(16));
console.log(
Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join(""),
);
// SHA-256 digest (returns ArrayBuffer)
const data = new TextEncoder().encode("Hello, Andromeda!");
const hash = await crypto.subtle.digest("SHA-256", data);
const hex = Array.from(new Uint8Array(hash))
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
console.log("SHA-256:", hex);Performance
const start = performance.now();
let sum = 0;
for (let i = 0; i < 1_000_000; i++) sum += Math.sqrt(i);
console.log(`Took ${(performance.now() - start).toFixed(2)}ms`);
// Marks and measures
performance.mark("sort:start");
const data = Array.from({ length: 10_000 }, () => Math.random());
data.sort((a, b) => a - b);
performance.mark("sort:end");
performance.measure("sort", "sort:start", "sort:end");
for (const entry of performance.getEntriesByType("measure")) {
console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
}SQLite
Andromeda ships SQLite via DatabaseSync (aliased as Database):
const db = new Database("notes.db");
db.exec(`
CREATE TABLE IF NOT EXISTS notes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
const insert = db.prepare("INSERT INTO notes (title, content) VALUES (?, ?)");
insert.run("First note", "Stored in SQLite!");
const rows = db.prepare("SELECT * FROM notes ORDER BY id DESC").all();
for (const row of rows as any[]) {
console.log(`- ${row.title} (${row.created_at})`);
}
db.close();In-memory databases are written new Database(":memory:").
Web Storage
localStorage and sessionStorage are persistent and backed by SQLite:
localStorage.setItem("theme", "dark");
localStorage.setItem("profile", JSON.stringify({ name: "Alice" }));
console.log("theme:", localStorage.getItem("theme"));
console.log("items:", localStorage.length);
sessionStorage.setItem("sessionId", crypto.randomUUID());
console.log("session id:", sessionStorage.getItem("sessionId"));HTTP Server
Build an HTTP service with Andromeda.serve (returns a Promise that runs
until aborted):
Andromeda.serve((req) => {
const url = new URL(req.url);
if (url.pathname === "/health") {
return new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
}
return new Response("Hello from Andromeda!");
});
// With options
Andromeda.serve({
port: 3000,
hostname: "0.0.0.0",
handler: async (req) => {
const body = await req.text();
return new Response(`echo: ${body}`);
},
});The HTTP server requires the runtime to be built with the serve feature
(enabled by default).
Scheduled Tasks (Cron)
// Cron-string schedule
Andromeda.cron("hourly-cleanup", "0 * * * *", () => {
console.log("Cleaning up at", new Date().toISOString());
});
// Or the structured-object form (UTC)
Andromeda.cron("every-six-hours", { hour: { every: 6 } }, async () => {
await doWork();
});Subprocesses
Spawn external programs with Andromeda.Command:
const cmd = new Andromeda.Command("echo", { args: ["hello", "world"] });
const out = await cmd.output();
console.log(out.stdout); // "hello world\n"
// Long-running
const child = new Andromeda.Command("sleep", { args: ["10"] }).spawn();
child.kill();Interactive REPL
andromeda replTry:
> 2 + 2
4
> crypto.randomUUID()
"…"
> const db = new Database(":memory:")
> db.exec("CREATE TABLE t (id INTEGER)")
> db.prepare("INSERT INTO t VALUES (?)").run(1)
> db.prepare("SELECT * FROM t").all()REPL commands: help, history, clear, gc, exit.
Modules and Imports
ES modules and TypeScript module resolution work out of the box:
// math-utils.ts
export function add(a: number, b: number): number {
return a + b;
}
export const PI = Math.PI;// main.ts
import { add, PI } from "./math-utils.ts";
console.log(add(5, 3), PI);
// Dynamic imports work too
const helpers = await import("./helpers.ts");Bare specifiers resolve through Import Maps defined in
your andromeda.json or referenced via import_map_files.
Toolchain
andromeda fmt # format current directory
andromeda lint # lint current directory
andromeda check # TypeScript type check
andromeda bundle main.ts dist/app.js
andromeda compile main.ts dist/app # single-file executable
andromeda task dev # run a task from andromeda.json
andromeda upgrade # update to the latest releaseSee the CLI Reference for full details.
Common Patterns
Error Handling
try {
const content = Andromeda.readTextFileSync("nonexistent.txt");
console.log(content);
} catch (error) {
console.error("Failed to read file:", (error as Error).message);
}Signals
Andromeda.addSignalListener("SIGINT", () => {
console.log("Got SIGINT, cleaning up…");
Andromeda.exit(0);
});Next Steps
- Browse the API Reference
- Configure your project with andromeda.json
- See the Examples
- Join the Discord community