Changelog
Versions follow 0.SPRINT.0 — the minor version is the sprint number, so the version tells you exactly how far the language has grown. Every version is a git tag.
Native codegen II — closures & the whole language
- Function values compile: closures carry their captures in the value runtime and dispatch through the module's function table via call_indirect — higher-order stdlib code (map/filter/fold), captured environments, and recursion through function values all ride the WASM call stack.
- args(), env(), read_file, and write_file reach the host through four runtime imports with the ptc run contract; a compiled media example writes a byte-identical PPM. CoW and the move optimization run the VM's own fast paths in compiled code.
- ptc build compiles every fixture: the differential suite now requires all 17 to compile and match ptc run byte for byte — the codegen boundary is gone, the CI gate is total. 485 cargo tests, 82 stdlib self-tests.
Native codegen I — values in memory
- ptc build compiles full-value programs to WASM: records, enums, lists, Maps, Sets, Text, Option/Result, match, for, with, the whole prelude, monomorphized generics, and multi-file programs — every reachable function becomes a real WASM function with real control flow, calls, and recursion.
- One canonical semantics: the module carries its lowered program and drives the committed value runtime — the reference VM's own exec_simple compiled to WASM, values reference-counted in linear memory. Never a second implementation.
- The differential suite is a CI gate: 14 of 17 fixtures compile and match the VM byte for byte; runtime errors carry the same teaching message and source position on both sides. Function values, args()/env(), and file I/O teach native codegen II (Sprint 52).
The publishing gate
- ptc pack validates a package end to end before it meets a registry: strict manifest (teaching with pkg.render's words), fmt --check over every module, a /// doc on every pub item, and every test green — at least one test required, so an untested package cannot pass.
- A green gate prints the exact index.ptr line to paste; a red one lists every problem and exits nonzero. Publishing stays a git push — the gate makes it safe.
- The example packages pass their own gate: imaging-extras and poster-tools gained test blocks, poster-tools carries its vendor/ directory, and CI packs both on every push.
Packages learn dependencies
- polytone.pkg version 2: a deps: section names required packages with minimum versions — same header discipline, teaching by version, v1 manifests keep parsing.
- ptc vendor resolves registries transitively: depth-first, cycles are teaching errors naming the chain, conflicting requirements name both versions — and vendor/vendor.lock records every package as name, version, needed-by, origin.
- poster-tools joins the live registry as the first package with a dependency — vendoring it pulls imaging-extras automatically; verified end to end against a local registry (cycle, conflict, and missing-dep errors included).
One point zero
- The compatibility promise, spec §27: the language surface, format versions, the stdlib pub API, bridge outputs, and the teaching contract are frozen for all of 1.x — new things may arrive, existing ones never change spelling.
- The spec survived a machine-checked drift audit — every POLYTONE code block runs through ptc check — and graduated to v1.0 (polytone-spec-v1.0.md, sections 1-27).
- ci.yml gates every push: compiler tests, clippy, the canonical-formatting corpus, all 81 stdlib self-tests, the committed browser runtime, and the site build.
The API reference that cannot drift
- A new API section documents every pub item of the standard library — 76 entries across 9 modules with signatures, field lists, and /// docs, filterable and wired into sitewide search.
- It is generated, not written: gen-api runs ptc doc over the stdlib and parses the toolchain's own .doc.ptw output, so the reference always matches the code that ships.
- Every stdlib page of the reference is also prerendered as a static page for bots and no-JS readers.
The compiler leaves the repo
- Native toolchain downloads for Windows, macOS (Apple Silicon + Intel), and Linux: ptc and polytone-lsp with the stdlib and examples in one archive — the stdlib is found automatically next to the binary.
- Built by CI on real runners on every version tag and delivered through the deploy chain to /downloads/ — stable filenames, SHA256 checksums, a VERSION stamp.
- The site gains a Download page with per-platform install steps, and the project gains its badge: counter-spinning tone rings around a still P.
3D gets color and a camera
- .ptm version 2: shapes take color #rrggbb or a palette name — the same color grammar as .pti — and the mesh carries one color per triangle. Version 1 files render unchanged; v2 surface under version: 1 teaches the bump.
- mesh.render_view — a software rasterizer written in POLYTONE itself: auto-framed orbit camera (yaw/pitch in degrees), perspective projection, painter's algorithm, two-sided key-light shading. The model domain gets a picture without a second bridge.
- The 3D viewer joins the site's tools: the document is a .ptm v2 file, dragging the canvas orbits the camera, and the session exports as .ptm, .obj, or a reproducing .pt program.
The language server
- polytone-lsp speaks JSON-RPC over stdio: live diagnostics with the exact teaching errors ptc prints — imports resolve through the real module loader with your unsaved buffer substituted.
- Hover shows signatures and /// docs, for local and qualified names like images.canvas.
- Golden-tested with scripted sessions; Neovim, Helix, and VS Code setup in lsp/README.md.
The live registry — Phase M6 opens
- This site IS the registry: packages plus a self-describing index.ptr under /registry/, published by git push through the deploy chain.
- ptc vendor <registry-url> <name> consults the index — unknown names teach with the list — then fetches manifest and modules over HTTPS into vendor/.
- Phase M6 planned toward v1.0: LSP, .ptm v2 + a POLYTONE-written 3D rasterizer, the generated API reference, CI + spec audit, then the compatibility promise.
Playground v2 — Phase M5 complete
- Multi-file tabs: every tab is an importable module — modules are finally teachable, with a dedicated example crossing a module boundary.
- Syntax highlighting over the editor, and error-line markers: teaching errors switch to the failing file's tab, highlight the line, and scroll to it.
- Share links carry the whole file set. Phase M5 done: fmt/doc, four tool v1s, forms, and this — every tool now edits its native format with real depth.
.ptw v3 — apps with input
- input and button blocks: a button submits every field above it as name=value arguments on its app address — state rides the address, apps stay pure functions of it.
- web.form_value decodes submissions; the HTML bridge stays script-free — the viewer hydrates clicks and Enter.
- The guestbook joins the demo site: sign it, and your entry rides the button address into the next render.
.ptv v2 + video editor v1
- Sprites move: local-coordinate sprite blocks tween across scenes per frame (pixel-exact endpoints) — and scenes speak .pti v4, including the new text op with its built-in 5×7 font.
- audio: references a .pta soundtrack — the codec stays pure, hosts resolve it, and films export as a .y4m + .wav pair.
- The editor v1: sprite cards, move entries with coordinate fields, a soundtrack editor, and a synced audio player. The demo film has a title, a flying ball, and a riff.
.pta v2 + sound studio v1
- Named pattern blocks with a song: chain (sample-exact expansion), swing in the header, per-voice ADSR envelopes, and ! accents — v1 documents sound identical.
- The studio v1: tracks with envelope presets (organ/pluck/pad), pattern chips, an editable song chain, accent cycling, and 16 or 32 steps.
- The .pta v2 document updates live under the grid — the sequencer still edits the format, now with real musical depth.
Image editor v1 — layers, brush, fill
- Layers are op groups: painter's algorithm makes the flattened session a valid .pti v3 document — verified byte-identical to the layered render.
- A real drag brush (polyline ops with live preview), flood fill (.pti v3's fill op), zoom, op-level undo — and .ppm import as the base picture.
- The new engine images.draw_ops applies format op lines onto any image — the primitive that makes layers and imports composable.
ptc fmt, ptc doc & ergonomics
- ptc fmt: the canonical formatter — comment-preserving, idempotent, and it refuses to write if a single token would change. The whole corpus is canonical now.
- ptc doc renders a module's pub API to a native .ptw v2 page — documentation output is dogfood, rendered by POLYTONE's own web codec.
- The deferred ergonomics landed: for iterates Maps (keys), Text, and Bytes; text[i] indexing; and block-bodied lambdas as the right side of a binding.
ptpkg v0 — Phase M4 complete
- Packages: a directory with a polytone.pkg manifest — same header discipline as every native format, with pkg.render in the stdlib as the teaching decoder.
- ptc vendor copies a package's modules into vendor/; imports resolve local → vendor/ → stdlib. The new Registry page curates the first packages.
- Phase M4 done, Phase M5 planned: the depth pass — ptc fmt/doc, editor layers and brushes, .pta/.ptv v2, forms, a multi-file playground.
.pti v2 — palettes & gradients
- palette: names colors for every drawing op, gradient fills run vertical, horizontal, or radial with exact endpoints, and polygon fills any shape from three points up.
- The image editor grew a gradient tool, a triangle tool, and a palette swatch row — sessions still export as byte-exact .pt programs.
- v1 files render byte-identically; the new surface in v1 documents teaches the version bump. The viewer's demo scene shows a gradient sky with polygon mountains.
.ptw v2 — inline media
- image, film, and sound blocks reference native documents by address — and the web viewer's demo site now shows its picture, film, and tune inline.
- The bridge embeds what it is given: to_html_bytes(doc, assets) turns supplied images into BMP data URIs — encoder and base64 written in POLYTONE, images' own bridge stays PPM.
- Film and sound links hydrate into inline players in the viewer; v1 pages keep rendering unchanged, and media blocks in v1 teach the version bump.
Generic records & enums
- record Pair[A, B] and enum Tree[T] — user types reach the level of the builtin Option/Result, with total construction inference and left-to-right sharpening.
- Unit variants infer from the expected type exactly like None: let t: Tree[Int] = Tree.Leaf. Qualified across modules, recursive types included.
- Runtime identity stays the base name — values print as Pair(first: 1, second: "one"); the VM never sees a type parameter. Plus lists.zip.
Generic functions
- fn index_of[T](xs: List[T], x: T) -> Int — type parameters with total call-site inference. No turbofish: one way to call any function; conflicts teach.
- Bodies check once with opaque parameters; monomorphization generates readable instances (index_of[Int]) transitively, across modules — the VM never sees a type parameter.
- The stdlib's first generics: lists.index_of, reversed, take, drop — and video.pt dropped its hand-rolled Text-only search.
Record update & moves — Phase M4 opens
- with: t with plays: t.plays + 1 — one canonical form for 'the same record, some fields changed', with full teaching errors and clean swap semantics.
- Move analysis in lowering plus a fused in-place field write: the img = set_pixel(img, …) rebuild chain is zero-copy now — 5.05 s → 0.068 s (~75×) on the sprint-22 benchmark.
- Snapshots stay safe: anything still shared copies exactly once, for the snapshot. 422 tests green.
The web viewer — Phase M3 complete
- The pragmatic own browser: an address bar and history over a site of native documents — .ptw pages interlink, media render through their codecs, external links open in a new tab.
- Apps: a .pt address runs the program — arguments ride in the address, the app writes a native document, the shell renders it. The counter demo proves the cycle.
- Phase M3 complete: runtime, playground, canvas output, and four tools — every one runs real POLYTONE in the sandbox.
Video editor v0
- The document IS the native .ptv: scene cards in the .pti drawing grammar with live sandbox thumbnails, and a play/fade timeline you can reorder.
- Render produces Y4M in ~420 ms and the canvas player plays it in-page — frame math and crossfade interpolation verified against the decoder.
- Three exports: .ptv, .y4m, and a .pt program that reproduces the film byte-exactly. Three of four planned tools are live.
Sound studio v0
- A step sequencer whose document IS the native .pta format — the text updates live as you toggle cells, and chords become extra voices automatically.
- Play renders through the audio toolkit in the sandbox (~550 ms for the demo riff) and the WAV plays in-page.
- Three exports: .pta, .wav, and a .pt program that reproduces the song byte-exactly.
Image editor v0
- A browser editor where every click runs real POLYTONE: draw tools, filters, undo — the UI generates programs against the images toolkit, the sandbox renders (~35 ms per operation).
- The session is code: export a .pt program that reproduces your picture from scratch — verified byte-exact.
- images.from_ppm_bytes — the export bridge's inverse, with teaching errors: existing pictures can enter the toolkit.
Canvas output
- Films play on the site: a Y4M decoder — the exact inverse of the video bridge — drives a canvas player with play/pause; verified frame-exact against runtime output.
- Live mode: the program re-runs as you type and still images repaint their canvas in place — the image toolkit becomes interactive.
- No browser drawing API in the language: written files are the output system — one bridge per domain; the host decides what a file means.
The playground
- This site now runs POLYTONE live: edit code, run it or its tests, and written media appear as previews — .ppm on a canvas, .wav in a player, .html in a sandboxed frame.
- Eight curated examples, args() input, and share links that carry the source in the URL.
- Fuel: an instruction budget turns infinite loops into teaching errors instead of frozen tabs.
The browser runtime
- POLYTONE runs in the browser: the reference VM plus the whole compiler front end compiled to WebAssembly (~740 kB) — one canonical semantics instead of a second implementation.
- Sandboxed by construction: file I/O hits an in-memory virtual filesystem — seed inputs, read written files back; a rendered .ppm goes straight to a canvas.
- Records, enums, closures, match guards, the embedded stdlib, args()/env(), and ptc-test-format test mode — verified by a 9-check Node smoke test.
Media CLI
- args() -> List[Text]: everything after the file on the ptc run command line — no argv[0] to skip, no separate count; main keeps its single canonical shape.
- env(name) -> Option[Text]: an unset variable is None — never an error, never a silent empty string.
- examples/ptrender.pt: one universal renderer that dispatches on the input extension and exports all five native formats through their bridges — the first real POLYTONE command-line tool.
Language pass: CoW, guards, re-exports
- Match guards: case Some(n) if n > 0: — a Bool condition with the pattern's bindings in scope; guarded arms never count toward exhaustiveness.
- Type re-exports: pub signatures may use imported pub types qualified (pub fn origin() -> geo.Point); importers get the whole reachable type surface installed transitively.
- Copy-on-write values in the VM: heap values share until mutated. Same value semantics, paid lazily — passing, snapshotting, and storing big values is O(1), and the accidental deep-copy class is gone for good.
.ptv — native video
- .ptv (POLYTONE Video) v1: named scenes in the .pti drawing grammar plus a timeline of play and fade entries — moving pictures as intent, never as frames.
- The single Y4M bridge writes an uncompressed stream that ffplay, mpv, and VLC open directly.
- The format family is complete at five: image, audio, 3D, web, video — every codec written and self-tested in POLYTONE.
Audio toolkit
- The Sound-Studio core: silence, tone, append, mix, gain, repeat — and the classic ADSR envelope, all composable in code.
- examples/compose.pt: a plucked melody over a sine bass, built programmatically and exported as WAV in under three seconds.
Image toolkit
- The Bildverarbeitung core: crop, flips, nearest-neighbor scale, clipped blit — and map_pixels with lambdas, driving invert, grayscale, and brighten.
- Two more VM fast paths (len() and field reads on locals) plus a copy-free .pti hot path: the full pipeline went from 105 seconds to 0.6.
- examples/process_image.pt: decode a native scene, filter, flip, scale, export — end to end.
.ptw — native web · Phase M1 complete
- .ptw (POLYTONE Web) v1: documents as a typed block tree — Block is a pub enum, the HTML bridge renders it with an exhaustive match and escapes everything, always.
- No markup strings, no injection by construction — an author cannot produce broken HTML.
- The format family is complete: .pti image, .pta audio, .ptm 3D, .ptw web — every codec written and self-tested in POLYTONE.
.ptm — native 3D
- .ptm (POLYTONE Model) v1: placed primitives — box, plane, UV sphere — plus raw triangles, as canonical text with the language's own float rule.
- The tessellator is stdlib POLYTONE; the single OBJ bridge exports for today's viewers.
- examples/first_model.ptm: six intent lines become 479 vertices and 859 triangles.
.pta — native audio
- .pta (POLYTONE Audio) v1: tempo, synth voices (sine/square/saw/triangle), tracker-style note patterns with holds and rests — musical intent as canonical text.
- The synthesizer is stdlib code written in POLYTONE: equal-temperament tuning, click-free envelopes, mixing — exported through the single WAV bridge.
- Float gains sin/cos, and the VM gains in-place fast paths for list building — indexed reads/writes and push on locals no longer copy.
.pti — the first native format
- POLYTONE adopts no existing media formats — every format is designed new, for LLMs: text never binary, intent over samples, teaching decoders (formats/00-OVERVIEW.md).
- .pti (POLYTONE Image) v1: a canvas plus pixel/line/rect/circle operations in one canonical text form — decoded and rendered by stdlib code written in POLYTONE.
- examples/first_scene.pti renders end-to-end through the single PPM export bridge; decoder errors name line numbers and canonical forms.
Bytes & file I/O
- The Bytes type: text.to_bytes(), list.to_bytes(), get/slice/to_hex — binary data with value semantics and hex display.
- read_file and write_file return Results — I/O can fail, so the failure is always in the type.
- POLYTONE wrote its first real image: examples/first_image.pt renders a binary PPM gradient — the opening move of the multimedia roadmap.
Standard library
- The stdlib is written in POLYTONE itself: ints, lists, and texts under stdlib/ — every module self-tested with its own test blocks.
- import falls back to the stdlib search path ($POLYTONE_STDLIB or the checkout's stdlib/); a local file always shadows the stdlib.
- lists.range(1, 6).map(fn(x: Int) -> Int = x * x) — the language now composes with itself.
Type exports
- pub record and pub enum cross module boundaries — qualified everywhere: shapes.Point, shapes.Color.Red, case shapes.Color.Rgb(…):
- Nominal identity program-wide: a Point built inside the module equals a shapes.Point built by the importer.
- Exhaustiveness works across modules and names missing cases in qualified form.
Modules & imports
- Multi-file programs: import mathx loads the neighbor file; pub fn is the export; access is always qualified.
- Cycles, self-imports, missing files, private access — all teaching errors; a typo lists the module's functions.
- This documentation site shipped — every output on it recorded from the real compiler.
Test blocks & assert
- Tests are a language construct: test "name": blocks plus the assert keyword, run via ptc test.
- Failing comparisons report both operand values: left = [1, 4], right = [1, 5].
Function values
- Lambdas as unnamed declarations (fn(x: Int) -> Int = x * 2), fn types, named functions as values.
- map, filter, and fold on lists; captures are by value and read-only.
Enums
- Sum types with unit and payload variants, always qualified (Status.Banned(reason: …)); exhaustiveness over every variant.
Standard prelude
- Built-in methods on core types; everything fallible returns an Option — no null, no exceptions.
Records & match
- Nominal records with one canonical constructor; match/case with enforced exhaustiveness.
WASM scalar backend
- ptc build compiles the scalar subset to WebAssembly, differentially tested against the VM byte for byte.
VM execution
- The PTIR stack machine: value semantics, checked arithmetic, source positions on runtime errors.
Type system
- Local inference, explicit function boundaries, no truthiness, no implicit conversions; Option/Result and the ? operator.
Parser
- The full grammar with teaching parse errors; pub requires a /// doc comment.
Lexer & spec
- Spec v0.1 and the lexer: 4-space blocks, string interpolation, word operators (and/or/not).