code-playpad

Two editors, one Python

Both playgrounds below import the same runtime module, so they share a single Web Worker and a single Pyodide instance. Run them back to back: the second one does not reboot anything. Run them at the same time and the second waits — Python is single-threaded, so runs are queued.

import sys print("widget A ·", sys.version.split()[0]) total = sum(range(1, 101)) total import time print("widget B — sleeping 2s, keeping the queue busy") time.sleep(2) print("widget B done")
Proof of the shared worker: press Run on B, then immediately on A. A shows Running… but only starts once B finishes. If each widget had its own Pyodide, both would boot ~11 MB separately.

Stopping runaway code

There is no cooperative interrupt without SharedArrayBuffer (which needs site-wide COOP/COEP headers), so Stop terminates the worker and boots a fresh one. Queued runs survive and land on the new worker.

while True: pass

Reading input

input() really blocks: the prompt appears, a caret waits in the output, and the program continues when you answer. What you type is echoed the way a terminal echoes it, so the transcript reads back properly. Enter sends a line, EOF ends input.

The standard-input box is optional now — lines in it are used up first, before you are asked. Handy for scripted exercises; leave it empty to answer live.

name = input("name? ") age = int(input("age? ")) print(f"hello {name}, next year you turn {age + 1}")

Prompting in a loop works the same way — press EOF or send a blank line to stop.

total = 0 while True: line = input("number (blank to finish)? ") if not line: break total += int(line) print("sum:", total)

Packages load themselves

loadPackagesFromImports() runs before your code, so an import is all it takes.

import numpy as np grid = np.arange(9).reshape(3, 3) print(grid) grid.sum()

Light and dark

Press theme in the header. Widgets default to theme="auto", which follows this page's switch — it sets color-scheme on the root, and that is all the widget needs. An explicit theme overrides it: the pair below stays put whichever way you toggle.

print("pinned light") print("pinned dark")

Sizing

height="auto" fits the snippet — no dead space under a one-liner, no hidden lines under a long one, at any viewport width.

print("auto: exactly as tall as it needs to be")

max-height caps a long listing and scrolls inside it.

WEEKDAYS = [ "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday", ] MONTHS = [ "January", "February", "March", "April", "May", "June", ] print(len(WEEKDAYS), "days and", len(MONTHS), "months")

min-height gives a short exercise room to type in — the whole box is the editor, not just the first line.

# Your code here

Lazy loading

↓ scroll — the widget below boots only as it nears the viewport ↓
print("I did not exist in the network tab until you scrolled here.")