Skip to content

GuideDay-to-day use. Assumes you've run Ganymede once.

Start Here ​

NOTE

One of the notebooks the apps' Open Example Notebooks copies, shown with the outputs it saved when ganymede-ui last ran it. The notebook is in ganymede-ui.

These notebooks show what a TypeScript notebook in Ganymede can do. Each one runs on the TypeScript kernel the app runs itself, so they need no server, only a network connection for the ones that load packages or data.

They were saved with their outputs, so you can read them before running anything. Select a code cell and press Shift-Enter to run it, or use Run All. Change anything: these are your copies.

NotebookWhat it shows
01 Start HereCells, state, and the kinds of output a cell can show
02 Web APIsfetch from public APIs, tables, progress that updates in place
03 Charts and Diagramsjupyter.chart with Vega-Lite and Observable Plot, d3, Graphviz
04 DuckDBSQL in the notebook with jupyter.sql, results as tables and charts
05 Generative ArtSeeded randomness, canvas and SVG, animation
06 WidgetsSliders and buttons that talk to the kernel
07 UI FrameworksReact, Preact and Lit components as output
08 DeviceThe device API: clipboard, haptics, dialogs, share and files

Cells keep their state ​

A cell's top-level declarations stay defined for the cells after it. The last expression is the cell's result.

ts
const moons = [
  { name: "Io", radiusKm: 1821.6, orbitDays: 1.769 },
  { name: "Europa", radiusKm: 1560.8, orbitDays: 3.551 },
  { name: "Ganymede", radiusKm: 2634.1, orbitDays: 7.155 },
  { name: "Callisto", radiusKm: 2410.3, orbitDays: 16.689 },
];
moons.map(m => m.name);
text
[ 'Io', 'Europa', 'Ganymede', 'Callisto' ]

TypeScript types are stripped before a cell runs, so annotate as much or as little as you like. await works at the top level.

ts
type Moon = (typeof moons)[number];
const largest = (list: Moon[]): Moon => list.reduce((a, b) => (b.radiusKm > a.radiusKm ? b : a));
await new Promise(resolve => setTimeout(resolve, 200));
console.log(`The largest moon is ${largest(moons).name}.`);
text
The largest moon is Ganymede.

Rich output ​

The kernel's jupyter object turns values into rich output. jupyter.table shows rows as a table: click a column's name to sort by it.

ts
jupyter.table(moons, { title: "Galilean moons" });
Galilean moons
name
string
radiusKm
number
orbitDays
number
Io1821.61.769
Europa1560.83.551
Ganymede2634.17.155
Callisto2410.316.689

jupyter.md shows Markdown, math included.

ts
jupyter.md`Kepler's third law: the square of the period grows with the cube of the orbit, $T^2 \propto a^3$.
Callisto's period is ${(moons[3].orbitDays / moons[0].orbitDays).toFixed(1)} times Io's.`;

Kepler's third law: the square of the period grows with the cube of the orbit, $T^2 propto a^3$. Callisto's period is 9.4 times Io's.

jupyter.svg draws with no library at all.

ts
const scale = 70 / largest(moons).radiusKm;
const circles = moons.map((m, i) => {
  const x = 60 + i * 150;
  const r = m.radiusKm * scale;
  return `<circle cx="${x}" cy="85" r="${r.toFixed(1)}" fill="hsl(${30 + i * 50} 55% 55%)"/>
    <text x="${x}" y="178" text-anchor="middle" font-size="14" fill="currentColor">${m.name}</text>`;
});
jupyter.svg`<svg xmlns="http://www.w3.org/2000/svg" width="580" height="190" font-family="system-ui" style="color: #888">${circles.join("")}</svg>`;

Output of the cell above (image/svg+xml)

jupyter.html shows HTML. Scripts in it run in a sandboxed frame once the notebook is trusted.

ts
jupyter.html`<details open>
  <summary><b>Galilean moons</b></summary>
  <p>Discovered by Galileo Galilei in January 1610.</p>
  <ul>${moons.map(m => `<li>${m.name}</li>`).join("")}</ul>
</details>`;
Galilean moons

Discovered by Galileo Galilei in January 1610.

  • Io
  • Europa
  • Ganymede
  • Callisto

A plain object shows as text. To pick the output type yourself, display a MIME bundle with raw: true; JSON shows as a tree you can fold.

ts
await jupyter.display({ "application/json": { moons }, "text/plain": JSON.stringify(moons) }, { raw: true });
json
{
  "moons": [
    {
      "name": "Io",
      "radiusKm": 1821.6,
      "orbitDays": 1.769
    },
    {
      "name": "Europa",
      "radiusKm": 1560.8,
      "orbitDays": 3.551
    },
    {
      "name": "Ganymede",
      "radiusKm": 2634.1,
      "orbitDays": 7.155
    },
    {
      "name": "Callisto",
      "radiusKm": 2410.3,
      "orbitDays": 16.689
    }
  ]
}

Output that updates ​

jupyter.display shows output while the cell is still running. Give it a display_id, and a later display with update: true replaces it.

ts
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
const bar = (pct: number) => jupyter.html`<progress value="${pct}" max="100"></progress> ${pct}%`;
await jupyter.display(bar(0), { display_id: "progress" });
for (let pct = 10; pct <= 100; pct += 10) {
  await sleep(150);
  await jupyter.display(bar(pct), { display_id: "progress", update: true });
}
100%

Errors ​

An exception stops the cell and shows where it was thrown.

ts
JSON.parse("{ not json");
text
SyntaxError: Expected property name or '}' in JSON at position 2 (line 1 column 3)
    at JSON.parse (<anonymous>)
    at eval (In[9]:1)
    at eval (In[9]:1)
    at eval (<anonymous>)
    at worker.js:2210:64
    at handle2 (worker.js:1798:29)
    at answer (worker.js:2219:16)
    at worker.js:2224:3

Packages ​

import loads packages from npm through the esm.sh CDN on first use; pin a version with npm:name@version.

ts
import { formatDistanceToNow } from "npm:date-fns@4";
const discovered = new Date("1610-01-07");
`Ganymede was discovered ${formatDistanceToNow(discovered)} ago.`;
text
'Ganymede was discovered almost 417 years ago.'

Where to go next ​

Open 02 Web APIs in the sidebar. The cdp-kernel docs describe everything jupyter offers.

MIT license.