Natter GitHub

Python interpreter for the browser

Natter, a small Python for the classroom

A Python interpreter written in JavaScript. It loads fast, has a debugger built in, and an endless loop never freezes the page. Made for teaching.

Status: nearly complete. The language, the standard library and the browser hosts (console, turtle, Kara, Pygame Zero, forms, numpy and plots, with an image library of about 900 sprites) work and are tested against CPython and the real libraries. Packaged for embedding as @natterjs/core, @natterjs/web and @natterjs/assets on npm, MIT licensed. Everything on this page is marked as in the base planned or not planned.

hello.py
1names = ["Ada", "Linus", "Grace"]
2for name in names:
3    print(f"Hi {name}!")
4
5print(len(nmes))
Output
Hi Ada!
Hi Linus!
Hi Grace!
NameError: name 'nmes' is not defined.
Did you mean: 'names'?
Illustration only.

Get started

Use Natter in your page

Three ways, pick one. Natter runs entirely in the browser; there is no server to set up.

  • CDN: one HTML file, no tools. Copy the example, open it in a browser.
  • npm: for projects with a bundler (Vite, esbuild, webpack) or for Node.
  • Download: the zip of the web build from the latest release; put the folder next to your page and import natter-web.js from it.

Packages: @natterjs/web (browser host), @natterjs/core (interpreter, runs in Node too), @natterjs/assets (images and sounds, browse them).

<!-- index.html: save, open in a browser -->
<div id="console"></div>
<script type="module">
  import { createRunner, createConsole } from
    "https://cdn.jsdelivr.net/npm/@natterjs/web@0.1/dist/natter-web.js";

  const runner = createRunner();
  createConsole(document.getElementById("console")).connect(runner);

  await runner.run(`
for i in range(3):
    print("Hello", i)
`);
</script>
# in your project
npm install @natterjs/web          # browser host (includes the interpreter)
npm install @natterjs/assets       # optional: images and sounds for games

# Node only, no browser
npm install @natterjs/core

@0.1 in the URL means “the newest 0.1.x”; use @0.1.1 to pin a version. A running program can be paused, stepped and inspected: runner.pause(), runner.step("over"), runner.inspect(), runner.stop().

// Node 18+, no browser: npm install @natterjs/core
import { runToEnd } from "@natterjs/core";
console.log(runToEnd("print(1 + 1)").output);   // 2

Images and sounds for Pygame Zero and Kara: createPgzero(el, { assets: { baseUrl: "https://cdn.jsdelivr.net/npm/@natterjs/assets@0.1/", catalog: "catalog.json" } }). Self-hosting: copy the whole dist/ folder (the start file loads its chunks from next to itself). Each GitHub release also has natter-web-<version>.zip and the npm tarballs, with checksums. More options, such as turtle, Kara, forms and plots, are in the reference.

01 · Why

Why another Python?

Browser Python normally means shipping a large runtime. Natter is a compact interpreter that implements the part of Python taught in school, and implements it so that the things teachers need are easy.

02 · Inside

How it works

  1. Source
  2. Lexer
  3. Parser
  4. Compiler
  5. Bytecode VM
A stack machine with its own frame stack
It does not use JavaScript recursion, so it can stop and resume after any instruction.
Budgeted execution
run(task, n) executes at most n instructions, then hands control back to the page.
Suspend and resume
A native function such as input() or an animated forward(100) can park the program and continue it later. Blocking calls look like normal Python.
Tasks
The main program, every event handler and every frame of a game loop are separate tasks with their own budget.
Host bridge
Modules (turtle, games, forms, plots) talk to the VM through host objects. The core knows nothing about the DOM.
Debugger
The compiler marks statement starts. The VM checks breakpoints and step mode there and can report variables and the call stack at any pause.

03 · Language

The language

A subset of Python 3 that behaves like Python and prints the same output. The aim is the part used in schools, documented exactly.

AreaContentsStatus
BasicsIntegers of any size, floats, strings with f-strings and format, lists, tuples, dicts, slices, if/elif/else, while, for with else, break, continue, pass, tuple swap a, b = b, ain the base
Functionsdef with defaults, *args, **kwargs, closures, global, nonlocal, lambda, decorators, unpacking, del, assertin the base
ComprehensionsList and dict comprehensionsin the base
Classesclass, __init__, inheritance, super(), __str__, __repr__, @property, operator methods such as __add__in the base
Exceptionstry/except/else/finally, raise, custom exception classesin the base
Sets, generatorsset, frozenset, yield, lazy generator expressionsin the base
Inputinput() (the page waits for the answer), with and context managers, sys.exit(), print(file=…)in the base
Filesopen() with read, write and append, os and os.path, import of your own files. The project lives in memory; the host keeps it where it likes (folder, ZIP, browser storage)in the base
Type hintsParsed and ignored, so code from books runsin the base
Threads, async, eval, execNot useful in a classroom and hard to make safenot planned

Errors carry the line, a call stack and a hint. Numbers behave like CPython: integers become big integers above 253, round() uses banker's rounding, // and % follow CPython.

04 · Modules

Modules

Each module is a separate package that talks to the core through the host bridge, so embeds only load what they use.

ModuleWhat it offersStatus
randomrandint, choice, choices, sample, shuffle, uniform, gauss, seed …in the base
stdlibtime (virtual clock, no real waiting), datetime, json, statistics, collections, itertools, functools, string, copy, sys, re, pprint, textwrap, and all of math (sin, cos, exp, log …)in the base
turtleDraw with a turtle, several turtles, colors, fill, speed. The movement and commands are done and match CPython; drawn in the browserin the base
karaA small ladybug that learns to move, turn and sense. The rules and commands are done; drawn in the browserin the base
pgzeroThe full Pygame Zero reference: draw(), update(), event hooks, Actor, Rect, keyboard, screen, images, sounds, music, clock, animate, tone, text formatting. Runs with or without import pgzrun. The game logic is done and matches real Pygame Zero 1.2.1; drawn in the browser by the web hostin the base
formsWindows with buttons, text boxes and lists using the guizero API (a subset), laid out on a grid that grows with its content. The widgets, layout and events are done and match real guizero; drawn in the browser by the web hostin the base
numpy, plotsA subset of numpy arrays (printing, indexing, statistics and random numbers match numpy) and matplotlib charts drawn as SVG. Done and compared with the real libraries; the page shows the figure through the browser hostin the base
requestsrequests.get(url).json() through the browser, for servers that allow it (CORS). The calls and the CORS explanation are done; the page does the actual fetchin the base
Server code, scraping, MLbottle, sqlite3, BeautifulSoup, kerasnot planned

The command reference lists every module, function, class and constant with its parameters, notes on deviations from CPython and a runnable example. It is generated from the interpreter's source, so it cannot drift from what Natter really does.

05 · Determinism

Same program, same output

Same math everywhere

Addition, multiplication and sqrt are exact IEEE 754. Functions that browsers round differently, like sin and exp, are implemented inside Natter so results are identical on every device.

Random seed

The seed is an optional parameter. Without it, the seed comes from the browser's secure randomness and every run differs, as in normal Python. With it, every run is the same. The seed that was used can be shown to reproduce a run.

Limits

Recursion depth 1000 like CPython, fixed limits for list and string size, independent of the browser's JavaScript stack.