SkyTales Visual Grammar

This document is law. Paste it at the top of every session that generates or edits a visualization. When an existing page conflicts with this document, the page is wrong.

The reference implementation of this grammar is brand/template.html — a working three.js coordinate system (2D top-down and 3D, axes/grid/vectors, an eased animation engine) that embodies the rules below. The template and this document must hold the same ideas: the template is where an idea is proven in code, this document is where it becomes law. When something in the template turns out better than what is written here, update this document to match it.

Internal Slogan

  • “underpromise over deliver”

The brand image is one picture: a neon orange jumpsuit against the black sky of a night jump. Everything below derives from it. The sky is the ground layer, quiet and deep. The suit is the subject, and there is only one suit per frame.

Provenance: the anchor colors are pixel-sampled from the Pro-Fly FOCUS fabric palette (NAVY #262C53, ORANGE #EB5C15), then adjusted for screen as documented per token. The fabric values are the identity; the screen values are the rendering.


1. Palette

Anchors

Token Hex Name Role
SKY.void #04060B Night sky General website background where content lives (cards, text). Pitch black with a breath of blue. Also the panel material: at 70-95% opacity with a backdrop blur, it is glass over the void — the fabric every panel, card, and drawer is made of.
SKY.simulator #000000 Deep void Background of every physics simulation canvas. Pure pitch black. The void makes the neons burn.
SKY.navy #262C53 Jumpsuit navy Rare fabric-texture accent only (a toggle track, a small chip) — never a panel, card, or drawer background. Sampled from the Pro-Fly Cordura swatch. A solid navy slab as a panel is the antipattern this palette explicitly rejects.
SKY.orange #FF6417 Suit orange THE subject. Fabric swatch is #EB5C15; screen version keeps the exact hue and saturation with value pushed to full so it reads neon on the void.

Supporting cast

Token Hex Name Role
SKY.cyan #38BDF8 Altitude cyan Secondary object; the thing the subject interacts with.
SKY.violet #8B7CF6 Ghost violet Reference traces, previous states, analytic solutions, “what the textbook predicts.”
SKY.green #34D399 Canopy green Positive, stable, converged, valid.
SKY.red #F43F5E Cutaway red Negative, unstable, diverged, error. Named after the emergency handle for a reason: rare and unmissable.
SKY.grey #64748B Component grey Individual components behind a sum. Thin, quiet, plural.

Text

Token Hex Role
SKY.textBright #F1F5F9 Headings, values, results
SKY.textMuted #94A3B8 Body, descriptions
SKY.textDim #475569 Labels, units, hints — sparingly

Contrast law. The sky is dark, so the foreground has to compensate by being decisively brighter than whatever sits behind it. Two consequences, both non-negotiable:

  1. Grey text is rare. Default to textBright. Drop to textMuted for genuine secondary prose. textDim is for tiny hints only — never for a control’s label, a value, or anything a user has to read to operate the sim. Grey-on-grey is the failure mode: an inactive segmented-control label rendered textDim on a grey track is unreadable and looks broken.
  2. Surfaces stay dark; they never drift up into mid-grey. A panel, track, pill or chip is void glass (rgba(4,6,11,0.7–0.95)) or a faint white lift (rgba(255,255,255,0.04–0.08)). The moment a surface reaches an opaque mid-slate like #475569, the text on it collapses into it. If you find yourself brightening text to rescue a surface, the surface is wrong, not the text.

When remapping a third-party numeric colour scale (e.g. Tailwind’s gray-300…gray-900) onto this palette: low numbers are text, high numbers are surfaces. Map 300/400 → textBright, 500 → textMuted, 600/700 → faint white lift, 800/900 → void glass. Never map a mid number to an opaque grey, because the same token gets used as both a background and a label.

Rules

  1. One orange per frame. Orange marks the subject: the wavefunction, the trajectory, the sum curve, the selected eigenvector. If two things are orange, neither is the subject. When the user grabs or edits something, it becomes orange while held.
  2. Never place orange and red as adjacent meanings; if a plot needs “error” next to the subject, the subject stays orange and the error is red, clearly separated by geometry or weight.
  3. Simulation backgrounds are pure pitch black (#000000), everywhere, no per-page mood variants. The website background (behind cards and text) is SKY.void (#04060B). A bright navy or indigo wash as a background is explicitly banned: it is the default AI-generated aesthetic and reads as templated. Panels, drawers, and cards are glass, not fabric: SKY.void at 70-95% opacity with a 12px backdrop blur and a hairline rgba(255,255,255,0.06) border — the site header (_includes/hero_header.html) is the reference implementation, and every other panel follows it. SKY.navy is reserved for small fabric-texture accents (a toggle track, a chip) and must never be painted as a panel’s full background. The data glows neon; the sky behind it — and beneath every panel — is void.
  4. Colors are vocabulary, not decoration. A returning visitor must be able to read any new SkyTales sim: orange = what we study, cyan = what it interacts with, violet = what theory says, grey = the parts, green/red = verdict.
  5. Black Objects on Black: Since the simulation background is pure pitch black (#000000), any black objects within the scene MUST have a visible outline or stroke (e.g., SKY.grey or a semantic color) so they do not disappear into the void.
  6. Brightness is hierarchy. The most important object burns brightest; everything less important fades toward the void by scaling its color down toward black (not by adding grey). Geometry that runs off to infinity under a projection dissolves into the background rather than terminating in a hard bright edge. Luminance reads as importance before color does.

2. Typography

Use Font Notes
UI, headings, body Ubuntu 300 body, 500 emphasis, 700 headings
Numbers, axis labels, units, readouts KaTeX Computer Modern Every number on the site wears the same face as the equations. Ubuntu Mono is explicitly banned for numerals — a value in a readout and the same value in an equation must look identical.
Code / file paths only Ubuntu Mono Mono is for code, not for measurements.
Equations KaTeX (default Computer Modern face) Every equation, no exceptions. No hand-styled italic HTML math, no images of equations.

Equations render white. All KaTeX output is SKY.textBright (#F1F5F9) by default — never grey, never muted, never a tinted “math colour”. The only symbols that deviate are the ones deliberately coloured by section 6: an object symbol wearing its scene colour (E is orange because the E field is the orange subject), or a parameter symbol turning SKY.orange while it is being touched. Everything else in the formula stays white.

.katex, .katex * { color: #F1F5F9; }   /* deliberate \textcolor{} still wins */

Numbers are set with the KaTeX font stack, which means the KaTeX stylesheet must be loaded on every page that shows numbers (it ships the Computer Modern faces):

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css" crossorigin="anonymous">
/* numbers, readouts, axis tick labels, units */
font-family: 'KaTeX_Main', 'Latin Modern Roman', 'Computer Modern', Georgia, serif;

Load (UI + code): https://fonts.googleapis.com/css2?family=Ubuntu:wght@300;400;500;700&family=Ubuntu+Mono:ital,wght@0,400;0,700;1,400&display=swap


3. Axes

Axes are instruments, not subjects. They never compete with the data.

Two different things are governed here and they are not the same: a 3D coordinate scene (a space the subject lives in) and a 2D data plot (a chart with quantities on it). The house style below is the law for scenes. Plots keep their own rule, further down.

House axes — white axes, orange subgrid

This is what a SkyTales 3D scene looks like. It is the default, not one of two equal options, and it holds unless the exception below is stated to apply. _visualizations/pure/quaternion.html and brand/template.html are the references.

  • Axes are white (SKY.textBright), thin but bold, drawn as anti-aliased fat lines (Line2), not 1 px Line. Never RGB = XYZ — that convention burns three colours on furniture.
  • Arrowheads are flat 2D triangles, never 3D cones. A shaded cone reads as cheap old-computer-graphics; the house arrowhead lies in-plane. Vectors are drawn slightly thicker than the axes so the subject reads above the furniture.
  • The subgrid is orange (SKY.orange): integer lines brightest (fat, ~30% opacity), a 2x2 minor grid per unit box (~10%), optionally a finer 2x2 inside each minor box. Drop the whole grid a hair below the axis plane so the white axes never z-fight it.
  • Numbers are white, large, in the KaTeX face, hugging the axis and nudged slightly right — never far-offset into empty space. Tick marks off by default; use them only on a bare axes-only scene.
  • z is up here — see section 4. The xy plane is the horizon and the grid lives on it.
  • Offer the scene at two altitudes: a 2D top-down orthographic view (rotation locked, pan/zoom only) and a 3D perspective view, switchable.
  • Every component — axes, numbers, labels, grid, finer grid, planes, vectors — is an independent one-click toggle, never a fixed preset.

How loud is loud. Within the house style there is a dial. When the coordinate system genuinely is the subject — linear algebra, a change of basis, a grid being transformed — the grid is allowed to dominate: brighter, denser, numbers on. When the coordinate system is only the room, the same white-and-orange grammar is drawn back: fewer lines, lower opacity, numbers off. It is one style at different volumes, not two styles.

Recessed axes — the one exception

Grey axes are not an alternative taste. They are for one situation: the scene has real depth layering and the axes would fight it. A translucent shaded surface, a volume, a trajectory threading front to back — something the viewer reads through.

When that holds, the axes go to the bottom of the layering, not merely to a duller colour:

  • SKY.grey at ~55%, thin, with small ticks carrying the scale so the axis line never has to shout
  • Numbers shrink to textMuted and default to off — the ticks already give the scale
  • Axis letters stay, muted: they cost nothing and name the space
  • Floor grid SKY.grey at 10%, fading with distance
  • Fog colour equals background colour, so geometry dissolves into sky, never into black

Greying an axis without pushing it back in the layering is a half-applied exception, and worse than not applying it — the white axes are lost and nothing is gained. If the axes still sit visually on top, use house axes.

The trigger is testable: does this scene have layering the axes would compete with? If no, house axes apply.

The subject then earns its focus by having mass: a solid, smooth, high-resolution surface reads as the subject in a way a wireframe never does. A sphere that is only latitude lines competes with the axes on equal terms, because both are just lines. Give it a translucent shaded surface (vertex-coloured, depthWrite: false) and it becomes an object.

Everything that belongs to the subject transforms with it. If a projection carries the subject’s lines outward, the surface morphs vertex-by-vertex through the same map so its colour field flows with them. Never fade a surface out because it is inconvenient to transform, and never leave it static while the rest of the object moves.

2D data plots (canvas / SVG)

A chart is not a coordinate scene. Orange gridlines behind a data curve fight the curve, so plots keep the quiet grammar:

  • Axis lines SKY.grey at 55%, 1 px. Gridlines SKY.grey at 12%, major only; no minor grid unless the sim is about precision reading.
  • The zero lines are brighter than the rest: SKY.grey at 35%, 1.5 px. Origin is information.
  • Tick labels: KaTeX Computer Modern, 11 to 12 px, SKY.textDim.
  • Axis titles and units: KaTeX Computer Modern, SKY.textMuted, units in parentheses: t (s), E (eV), Re(z).
  • Arrowheads on axes only when the plot is about direction. Data plots get closed frames or bare lines, not arrows.
  • No axis is ever orange, cyan, red or green. Axes have no allegiance.

3D render distance

Three.js clips geometry beyond the camera’s far plane and OrbitControls.maxDistance. Both default far too low, which cuts the scene in half when the user zooms out. Every SkyTales 3D sim must set:

  • Camera far plane: 100000 minimum (new THREE.PerspectiveCamera(fov, aspect, 0.1, 100000)).
  • Logarithmic depth buffer: enable { logarithmicDepthBuffer: true } on the WebGLRenderer to avoid z-fighting at large distances.
  • OrbitControls.maxDistance: 50000 or higher. The user must be free to fly far out and still see the scene.
  • No fog by default. Fog hides geometry that the user paid render distance to see. If fog is used for artistic effect, it must be optional in the VIEW drawer and off by default. The void is the background, not fog.
  • No FogExp2. Exponential fog falls off unpredictably and eats half the scene. If fog is ever needed, use linear THREE.Fog with an explicit near/far so the designer controls exactly where geometry fades.

4. Coordinate conventions (how x, y, z behave)

Every SkyTales sim uses the same physical frame, so intuition transfers between sims:

  • Right-handed. y is up. x points right, z points toward the viewer at the default camera. This is three.js native, so the code never fights the engine.
  • Gravity, when present, acts along −y. Things fall down the screen. This is a skydiving brand; down is sacred.
  • 2D sims live in the xy plane: x right, y up, standard math orientation. Angles are counterclockwise from +x, in radians. Degrees may appear in a readout beside radians, never instead of them.
  • Complex plane: Re on x, Im on y. A rotating phasor turns counterclockwise for positive ω.
  • Time, when plotted spatially, runs along +x (2D) or is the animated dimension (3D). Never plot time on a vertical axis.
  • Default 3D camera: slightly above the horizon looking gently down, the view of the ground from under canopy. Not top-down, not worm’s-eye.

Abstract / coordinate visualizations deviate deliberately. When a sim is about the coordinate system rather than a physical scene — no gravity, no floor (linear algebra, quaternions, complex analysis) — it uses the math convention: right-handed with z up, the xy plane as the horizon. The right-hand rule names the axes: x = index finger, y = middle finger, z = thumb pointing up. Physical sims keep y-up with gravity along −y; abstract sims use z-up. brand/template.html is z-up for exactly this reason.

If a domain has an unavoidable conflicting convention (e.g. medical imaging axial slices), the sim states the deviation in its EQUATIONS drawer in one sentence. Silent deviation is a bug.


5. Menus: the sorting rule and the three drawers

One rule decides where every control lives: does its symbol appear in the equations?

  • Symbol appears in the math (q, v, ω, ρ, b, T) → PHYSICS drawer.
  • Exists only in the renderer (arrow scale, sampling density, camera, trail length, glow, quality) → VIEW drawer.

If a control needs a “visual only” disclaimer, it is in the wrong drawer. Placement is the disclaimer. Physics controls changing how the sim looks is not mixing; that is the sim working. The only mixing that matters is renderer knobs sitting beside physical parameters, and this rule separates exactly those.

Fixed drawer names, fixed order, every sim:

  1. EQUATIONS. Read-only. The governing equations and the sim’s working formulas, KaTeX, textBright, display mode, one equation per block. Deviations from house conventions stated here in one sentence. This is the study copy of the math; the working copy is the touch overlay (section 6).
  2. PHYSICS. The one dedicated symbols menu. Every parameter that has a symbol in EQUATIONS, and nothing else. Slider label = the symbol (matching the KaTeX exactly) + name + unit in parentheses, Ubuntu Mono.
  3. VIEW. Everything that cannot change the state of the system. Nothing in this drawer may alter the math.

Presets live in the drawer whose controls they set. A preset that changes ω is a PHYSICS preset; a preset that changes the camera is a VIEW preset. A preset that does both does not exist; split it.

Inside drawers: section headers Ubuntu Mono, 11 px, letterspaced uppercase, textDim. Collapsible sections with chevrons; state remembered per session. No numbered bold lists for equations; spacing and dim mono labels (GAUSS, FARADAY) do the separating. No colored subheaders. Explanatory notes: Ubuntu 300, textMuted, two sentences maximum; longer is a lesson, not a panel.

Drawer behavior: SKY.void glass panel per section 7 (opacity + blur, never a solid navy slab), slides over the sim as an overlay. The sim never resizes and never pauses for a menu. Width around 320 to 360 px desktop, full-width sheet on mobile. Esc closes.

The icon rail: at rest, transparent icon only, SKY.grey, no background, no glow. Hover surfaces a SKY.void glass button behind the icon and brightens it; color and depth change, never size. The open drawer’s icon is SKY.orange. One drawer open at a time, therefore one orange in the chrome, automatically. Same rail position on every sim.

Implementation.

  • EQUATIONS and PHYSICS are custom SkyTales components, written once and imported by every sim like skytales.js. PHYSICS cannot be a stock library: its labels are KaTeX symbols and its sliders drive the live link and the touch overlay (section 6). Each sim declares its parameters as data (symbol, name, unit, range, textbook band, value); the shared component renders them.
  • VIEW is lil-gui, skinned once by a shared stylesheet (SKY.void glass panel, grey tracks, orange touch, Ubuntu Mono values, 12 px radius). Stock lil-gui never ships; “works by default” covers behavior, not costume.
  • The library is the disclaimer: a control sitting in lil-gui is by construction non-physical. If a symboled parameter ever ends up in lil-gui, the sorting rule has been violated mechanically and visibly. lil-gui cannot render a KaTeX symbol, and that limitation is load-bearing.

6. Symbols and the touch overlay

Symbols are the crosslink between drawer, equation, and scene. One notation everywhere: the ω on the slider is the ω in the KaTeX is the ω in the overlay.

Two classes of symbol:

  • Object symbols name a rendered thing and wear its scene color, always: if the E field is the orange subject, E renders orange in every equation; B cyan; a reference trace violet. Equation and scene share a color because they share a referent.
  • Parameter symbols are scalars (ω, q, b, T). Neutral textBright at rest, orange only while touched. This is what keeps a fifteen-slider PHYSICS menu from becoming a circus: at rest it is monochrome instrument; color appears only at the point of contact.

The live link. Grabbing a control turns its symbol orange in the slider label and in every rendered equation simultaneously; the rest of the equation drops to muted while held. Contrast enforces one subject even when an object symbol shares the formula. Release restores rest state. The user answers “which term am I bending right now” without reading a word.

The touch overlay. The permanent home of the math is the EQUATIONS drawer. While a parameter is being dragged, the single equation containing it fades in as a bare KaTeX heads-up readout over the sim: no panel, no background, fixed edge position (same position on every sim), active symbol orange, live value beside it in mono, everything else muted. Appears on grab, lingers about two seconds after release, fades. Click to pin, click again to release; pinned is the user’s choice, transient is the default. Never mid-screen, never a card.

For long equations (a Fourier sum, a Hamiltonian), the overlay shows the line or term containing the touched symbol, not the whole wall. The formula appears exactly when it matters, which is exactly and only while its parameter is being touched.


7. Chrome (controls and panels)

  • Panels: SKY.void at 70-95% opacity with a 12px backdrop blur — glass over the void, per section 1 rule 3 (the site header is the reference implementation) — 1 px border rgba(255,255,255,0.06), 12 px radius. Never a solid SKY.navy slab.
  • Sliders: track SKY.grey at 25%, filled portion and thumb SKY.orange. The slider is where the user touches the physics; touch is orange.
  • Buttons: default state a translucent lift on the panel with muted text; the single primary action per panel is orange. Two orange buttons in one panel is a contradiction in terms. Reset is never orange.
  • Orange marks a button, it does not fill it. An active or primary button is orange text with a 1 px orange outline on the normal translucent surface — never a solid orange slab. Filled orange buttons put large blocks of the subject colour in the chrome, and a row of them drowns the one orange that belongs to the data. The fill is reserved for the scene; the chrome only ever gets the outline.
  • Toggles: on = SKY.orange, off = SKY.grey at 25%. Green is a verdict, not a switch state.
  • Value readouts: KaTeX Computer Modern, bright.
  • Parameter ranges: sliders expose the full interesting range, past the textbook limits. Mark the textbook-safe region with a subtle tick or shaded band on the track if useful; never clamp to it.
  • Data colors are never chrome decoration. Chrome speaks grey, navy, and text tones; color on a symbol is sanctioned only by section 6 (object color or touch).
  • No persistent glow, ambient pulse, or hover scale on idle chrome. No header blocks that repeat the sim title. No generic subtitles; the sim demonstrates what it is.

The home button

Every sim carries exactly one route back to the site, and it is the same object in the same place on all of them: the SkyTales mark, top-left, from _includes/home-button.html.

  • The top-left corner is reserved for it. No sim title, menu, version chip, toggle or status pill occupies that corner. A sim that needs a title starts it clear of the slot.
  • It replaces, it never stacks. A sim that draws its own mark, wordmark, atom, infinity sign or any other placeholder in that corner gives the slot up — the home button takes its place and the placeholder is deleted from the sim. Two marks in one corner is the failure this rule exists to prevent, and it is currently shipping: maxwell-equations draws two, chaos-mandlebrot keeps an infinity sign beside it, quantum-optics an atom, vector-potential-interference and logic-net collide with their own headers.
  • Placeholders are not a style choice. An icon that stood in for the home button during development is not decoration once the real one exists. It goes.
  • It is chrome, so section 7 applies in full: grey at rest, no glow, no ambient pulse, no hover scale — colour and depth change on hover, never size.
  • Floating it over whatever happens to be there is the fallback, not the design. When a sim has a header, the button belongs in that header, laid out with it.

8. The javascript palette

Every new sim imports this. No hardcoded hex values in visualization code.

// skytales.js — single source of truth
window.SKY = {
  void:       '#04060B', // Website background
  simulator:  '#000000', // Simulator background
  navy:       '#262C53',
  orange:     '#FF6417',
  cyan:       '#38BDF8',
  violet:     '#8B7CF6',
  green:      '#34D399',
  red:        '#F43F5E',
  grey:       '#64748B',
  textBright: '#F1F5F9',
  textMuted:  '#94A3B8',
  textDim:    '#475569',

  // semantic aliases — prefer these in sim code
  subject:    '#FF6417',
  secondary:  '#38BDF8',
  reference:  '#8B7CF6',
  component:  '#64748B',
  stable:     '#34D399',
  unstable:   '#F43F5E',

  axis:       'rgba(100,116,139,0.55)',
  axisZero:   'rgba(100,116,139,0.35)',
  grid:       'rgba(100,116,139,0.12)',

  // panel material — SKY.void as glass, not SKY.navy as a slab
  panel:        'rgba(4,6,11,0.7)',   // resting panel background
  panelScrolled:'rgba(4,6,11,0.95)',  // panel background once content is behind it
  panelBlur:    '12px',
  panelBorder:  'rgba(255,255,255,0.06)',
};

9. Scope: law and voice

This grammar binds sims. Editorial pages (home, about, support) are voice: expressive layout and color are allowed there, as long as they never contradict the vocabulary (green still cannot mean “theme,” navy still cannot be a background).

Titles may take orange when the named thing is the subject of its page: SKYTALES on the home page is the subject of the home page, so an orange title there is the one-orange rule applied, not broken.

If a sim genuinely needs to deviate from this document, it states the deviation in its EQUATIONS drawer in one sentence. The bug is silence, not deviation.


10. Checklist for any new or edited sim

  • Sim canvas is SKY.simulator (#000000); surrounding page is SKY.void; navy backgrounds are banned
  • Panels/drawers/cards are SKY.void glass (opacity + blur), not a solid SKY.navy slab
  • Exactly one orange subject
  • Black objects on black backgrounds have a visible outline
  • Axes grey, labels in KaTeX Computer Modern with units
  • Every number (readouts, tick labels, units) uses the KaTeX Computer Modern face — never Ubuntu Mono
  • Right-handed, y-up, gravity down
  • Equations in KaTeX
  • Colors referenced via SKY.*, no raw hex
  • Sliders reach past the textbook range
  • Every control sorted by the symbol rule: EQUATIONS / PHYSICS / VIEW, no “visual only” disclaimers
  • One symbols menu; slider symbols match the KaTeX one to one
  • Object symbols wear scene colors; parameter symbols neutral until touched
  • Touch overlay: grab shows the relevant equation at the fixed edge position, symbol orange, gone after release
  • Icon rail: transparent at rest, navy on hover, orange only when open; sim never resizes or pauses for a menu
  • Home button top-left, from _includes/home-button.html; the corner is reserved for it and any placeholder mark that was there is deleted, not kept alongside
  • A returning visitor could read this sim, and its menus, using only the color vocabulary