astroterm — terminal planetarium (braille star-density rendering)
Batch 8 — rendering / screensaver / ambient-display cluster.
What it is
Section titled “What it is”astroterm renders a real-time star map in the terminal — stars, planets, constellations, lunar phase — for any date, time, and geographic location. It computes true celestial positions from catalog data (Yale Bright Star Catalog, NASA JPL planetary elements) and projects them into the terminal window, optionally drawing an azimuthal grid overlay, constellation stick-figures, and labels. Configuration is rich and all on the CLI: --latitude / --longitude / --city, --datetime, --threshold (magnitude cutoff), --fps (default 24), --speed (animation multiplier), --color, --constellations, --grid, --unicode, --braille, --aspect-ratio.
It is the cluster’s reference for spatial single-color rendering — placing discrete points and lines into a grid with correct geometry — as opposed to neo’s full-field cascade or chafa’s raster mosaic.
KN-86 rendering relevance
Section titled “KN-86 rendering relevance”Two techniques transfer directly, both read out of the source.
1. Magnitude → glyph-density ramp (the star brightness encoding). astroterm maps each star’s apparent magnitude onto a 10-step glyph ramp (src/core.c):
const char mag_map_round_ASCII[10] = {'0','0','O','O','o','o','.','.','.','.'};const char *mag_map_unicode_round[10] = {"⬤","●","⦁","•","•","∙","⋅","⋅","⋅","⋅"};A star’s magnitude is linearly mapped (map_float_to_int_range, min −1.46 to max 7.96) onto an index 0–9. Bright stars get the densest glyph (0, ⬤); faint stars get the sparsest (., ⋅). This is brightness-as-glyph-density — the exact single-color lever the ASCII Effect Spec ramp formalizes, here applied to point sources instead of a raster. The unicode ramp also shows a size progression (filled large circle → mid dot → tiny dot) that reads as brightness even at one color.
2. Braille sub-cell density for lines. Constellation lines are drawn with --braille via a per-cell 2×4 dot accumulator (src/drawing.c). Each terminal cell holds an 8-bit mask; the renderer ORs dots into braille_layer[y][x] and emits the corresponding U+2800-block glyph:
utf8[0] = 0xE2;utf8[1] = 0xA0 | (mask >> 6); // top 2 bitsutf8[2] = 0x80 | (mask & 0x3F); // bottom 6 bitsBraille gives 2× horizontal and 4× vertical sub-cell resolution — a 128×75 cell grid becomes a 256×300 dot grid for line work — without leaving the text grid and without any color. Lines that would look blocky in box-drawing characters render smooth. This is the single most important density technique in the cluster after the brightness ramp: braille is how you get sub-pixel detail out of a monochrome cell grid.
The architecture is a clean core / render split worth noting: core_position.c (astronomy + projection math) → core.c (catalog tables, magnitude→glyph mapping) → core_render.c (what to draw) → drawing.c (primitives: ASCII line, smooth box-drawing line, braille line, braille cell). Geometry, data, and rasterization are separated — the same split nOSh wants for any spatial scene.
Single-color adaptation
Section titled “Single-color adaptation”astroterm is already monochrome-native — --color is optional and the default render carries full meaning in glyph choice and position alone. That is the whole lesson: a star map needs no color because shape, position, and glyph density already encode everything (which star, how bright, what constellation). For KN-86:
- Drop color entirely; keep the magnitude ramp. On the amber deck, the 10-step glyph ramp is the brightness channel. Optionally pair it with the panel’s amber-intensity LUT (brightest stars also rendered at higher luminance), doubling the brightness cue — glyph density and phosphor intensity — which reads beautifully at one hue.
- Braille for all line/curve work. The azimuthal grid, constellation lines, and any orbital/trajectory overlay should use the braille accumulator. This is the canonical answer to “how do I draw a smooth diagonal on a monochrome character grid.” It composes with ADR-0027’s half-block 128×150 pseudo-pixel canvas: braille (2×4) for fine lines, half-blocks (1×2) for filled regions — two complementary sub-cell schemes, both single-color.
- CP437 ramp variant. astroterm’s unicode star glyphs (
⬤ ● ⦁ •) aren’t all in CP437. A KN-86 port substitutes code-page-safe density glyphs from the Press Start 2P + CP437 set (Canonical Hardware Spec) for the densest steps.
Proposed Deckline ambient-mode scene (astroterm as seed)
Section titled “Proposed Deckline ambient-mode scene (astroterm as seed)”astroterm is the starfield scene in the ambient/screensaver mode architecture proposed under neo. A slow drift of the operator’s current sky — or a fictional Kinoshita-world skyline — is a strong idle visual: low motion, high atmosphere, cheap to render. Concretely:
- Seed the field once, animate the drift. Project a star field into the 128×75 grid; advance the projection time slowly (
--speed-style multiplier) so the field rotates gently. Braille constellation lines fade in/out. This is far cheaper than neo’s full re-render every frame — the field is mostly static between low-rate updates — making it the lowest-draw ambient scene, ideal for long idle. - Seedable by deck state. The starfield can be seeded from the operator handle / deck state (Universal Deck State) so each operator’s idle sky is subtly their own — a small identity touch that costs nothing.
- Spatial engine reuse beyond ambient mode. The core/render split (project → place → braille-rasterize) is a reusable nOSh capability, not just an idle toy. Any cart wanting a map, radar sweep, orbital display, or node graph wants this same pipeline. Worth factoring the braille line/cell primitive into the nOSh render layer where carts can reach it via NoshAPI.
Relation to the (reveal …) visual language
Section titled “Relation to the (reveal …) visual language”A starfield is a natural :radial (reveal …) surface — stars appearing outward from a center reads as the sky coming into being, which is exactly the :radial “birth” semantics in the canonical grammar (ADR-0033). Ambient-mode entry could reveal the field with :radial; exit collapses it with (unreveal :radial).
- No public library API — astroterm is a binary, not a linkable library. The value is technique (magnitude ramp + braille accumulator + core/render split), reimplemented in nOSh’s own C. Same posture as the rest of the cluster.
- Braille is the headline takeaway and feeds the whole project: the U+2800 2×4 accumulator pattern belongs in the nOSh render layer as a shared primitive, cross-referenced from ascii-effects.md under density techniques.
- Performance is favorable. “Lightweight and fast ASCII rendering”; the field is mostly static between updates, so the effective frame cost is low — well within Pi Zero 2 W budget for an idle scene.
- Cross-link neo — the other ambient-mode scene (rain) and the shared screensaver-mode architecture.
- Cross-link libcaca — libcaca rasterizes imagery to cells; astroterm rasterizes geometry to cells. Complementary engines.
- Cross-link ascii-effects.md — the brightness-as-density ramp here is the same lever the spec’s default character ramp formalizes; braille belongs in that doc’s density-technique catalog.
- Cross-link react-video-ascii —
charMode: shape(glyph silhouette matching) is the raster analog of astroterm’s per-point glyph-density selection.