# glslop authoring: file → inspect → capture → publish The zero-dependency `glslop.mjs` v2.3 needs Node 18+ and an installed Chrome, Edge, Chromium or Brave. Download the single file from https://glslop.com/glslop.mjs. No npm install or Playwright is needed. The browser runs glslop's current canonical engine; this is local rendering, **not offline rendering**. For a development deployment, pass `--base http://127.0.0.1:4321` to every command. The v2 preview requires the matching `/render/workbench` endpoint; an older deployment only supports render/submit. ## Start and inspect (no account or key) ```sh curl -O https://glslop.com/glslop.mjs # In PowerShell, use curl.exe rather than its historical alias. node glslop.mjs doctor --json node glslop.mjs preview landscape.fs --compare landscape-v1.fs --out-dir captures node glslop.mjs render landscape.fs --width 1280 --height 720 --time 6 --out wide-t6.png --benchmark --json ``` `doctor` actually renders a test shader and reports the selected renderer. `auth: true` means a key is configured, **not that its validity or posting permissions were tested**. GPU-path timeouts never silently retry the same expensive shader in software. Use `--renderer gpu|software` to choose a path, `GLSLOP_BROWSER` to choose a browser executable, and `--timeout 180` for particularly slow compiles (default 90 seconds, maximum 600). Browser sandboxing stays enabled; `--no-sandbox` is an explicit escape hatch only for isolated containers that require it. Without the CLI, https://glslop.com/preview opens a local file in your browser. No source is uploaded to the server. “Prepare submission” carries the last successfully tested source to the existing form; that is not a publish action. Live workbench requires WebGL2/OffscreenCanvas workers; unsupported browsers get an error, never a surprise main-thread compilation. Workers share the browser's GPU process and do not provide driver-wide isolation. Closing a compiling viewer can still stall some drivers. ## Watch the agent work (optional) Skip this if you just want to render, preview or publish. Ordinary `render` / `iterate` calls save only their requested poster/frames: no journal, iteration field, viewer, background process or extra review steps. Plain `preview` keeps its working-file controls and creates no journal directory. Starting `watch` enables recording for that session **while the viewer server is running**. Renders in the same directory then preserve exact source, immutable PNG(s), hashes, settings and diagnostics in `.glslop/session`, including failed attempts. Stopping the viewer ends implicit recording; saved artifacts remain. Explicit `--session dir` / `GLSLOP_SESSION` enable recording without a viewer; `render --note` / `--note-file` also explicitly request a checkpoint. `--no-history` always wins. It reuses the render needed for visual review, not another GPU job. `submit`/`--dry-run` remain separate. ```sh node glslop.mjs watch --detach --reference ref.png --json # reference optional; returns immediately node glslop.mjs render landscape.fs --out v2.png --json # includes iteration.id node glslop.mjs feedback --note "Silhouette improved; reflection too sharp." node glslop.mjs feedback --image comparison.png --label "Reference / render" ``` The viewer follows completed checkpoints and later review notes automatically. Select an older iteration to pause following, pin a baseline, compare against references or select captured frames. Different dimensions/time/inputs/capture settings produce a comparison warning. The same URL has **Iterations** and **Live workbench** views. The latter embeds the existing worker-backed FsPreviewer for motion/controls; it loads only when explicitly selected. Switching to Iterations pauses playback without discarding/recompiling the worker. Selecting a different checkpoint in Live workbench serially loads its exact source, time and inputs. `preview` opens this same session shell initially in Live workbench; `watch` starts in Iterations. Start either once, not both. Both support `--detach`; `watch shader.fs` can optionally expose an explicit working file as well. For `preview` plus automatic checkpoint recording, explicitly select `--session .glslop/session`; plain preview does not enable it. A working-file edit still respects the workbench's auto-reload toggle. It never scrapes agent conversations or infers private reasoning. Feedback is an optional short observed review; `render --note` can record what changed without a separate call. `--note-file` accepts a UTF-8 review file (up to 8192 bytes). Existing comparison images can attach to that same ID; references/evidence accept PNG/JPEG/GIF/WebP up to 8 MiB, not executable SVG or HTML. ### User commentary and live evidence **Your comment** in either view saves a user-labelled annotation on the selected iteration. Focusing the editor pauses following and binds the draft to that checkpoint; new results or a later Follow latest click cannot retarget that draft. The target is shown explicitly. Failed saves retain the text. Comments are local, not public metadata and not automatically delivered to the agent's conversation. A CLI client can read that checkpoint's comments/reviews with: ```sh node glslop.mjs feedback --json # no note/image flags: reads, does not append ``` A live workbench capture keeps the **applied** checkpoint ID. Its PNG/source/settings bundle is saved as before, and its image attaches as evidence to that same checkpoint even if the agent has since produced a newer one. Working-file captures remain unbound until a recorded checkpoint is selected; live control tweaks never mutate the saved checkpoint's original evidence. A stepped-capture checkpoint opens live at its last captured time using the workbench's normal poster warm-up, not the recorded simulation history; that difference is labelled. Inspect exact captured frames in Iterations. `--session dir` overrides `GLSLOP_SESSION`, then the default `.glslop/session` in the working directory. Separate tasks should use separate directories; restarting with the same directory resumes its history. Unique publish-last bundles and explicit feedback IDs support concurrent writers without "latest" races. `--no-history` disables recording for batch/oracle use. A history-write failure warns without changing the render outcome. Artifacts accumulate until you delete the session directory; ignore `.glslop/` in your working project's git rules. Only explicitly saved session assets are served over token-protected loopback HTTP, never `.env`, keys or arbitrary project paths. Notes/references are not uploaded or attached to public submissions. Keep the capability URL private. Stop a detached viewer with its **Stop viewer** button; without `--detach`, Ctrl+C also stops it. Closing the tab alone leaves the server running. On SSH/containers, use `--no-open --port N` with loopback port forwarding, or skip the viewer: recording needs no open UI. Saved history can be viewed offline without GPU/browser automation; new renders still use the hosted engine. Dead/crashed viewers do not enable implicit recording; concurrent viewers own separate activation markers, so stopping one cannot disable another. This filesystem workflow is available to any agent that invokes the CLI (or wraps it as iterate/review tools); direct render API callers, browser-only preview, chat drafts and hosted apps keep their existing workflows and do not implicitly write a local journal. ## The iteration workspace The CLI serves only the explicitly selected source files on `127.0.0.1`, behind a random session token. It does not serve your project directory, `.env`, metadata files or API key. Keep the session URL private. The hosted iframe receives the source in browser memory, not through an upload API. It is embedded in the shared session shell; use the Live workbench view (the default for the `preview` command). - Source saves are detected, but **reload is explicit by default**. Enable “reload on save” deliberately. In-flight compiles finish; rapid edits do not spawn an unbounded pool of compilers or terminate one mid-compile. `--no-watch` freezes the initial source snapshot; `--no-open` prints the URL without opening a browser. Stop the local server with Ctrl+C. - Select square, widescreen or portrait presets, or enter custom dimensions (1–4096 per side, maximum 4 megapixels). Render resolution is explicit and independent of the browser viewport/DPR. - Play explores motion. Pause, enter a time, then **seek / reset history** for a deterministic still. Persistent shaders use the existing 120-step poster algorithm, not arbitrary simulation scrubbing. See https://glslop.com/rendering.txt for the exact time/history contract. - Captures are named. Pin a capture as A, then capture B after an edit or version switch. Mismatched size/time/input settings are flagged. Captures are still comparisons, not simultaneous live renderers. - CLI captures write `captures//image.png`, `source.fs` and `record.json`. The record includes exact input values, time, dimensions, source/image SHA-256, compile duration, effective target formats/filtering and capture mode. Existing names are never overwritten. In standalone browser preview, download a PNG or a self-contained JSON record instead. - “Benchmark” (or `render --benchmark --json`) reports **draw plus synchronized pixel-readback wall time**, after three warm-up draws, with twelve samples and median/p95. It includes synchronization/readback overhead and is neither pure GPU time nor a general FPS promise. It restores the deterministic still afterward. Matching workbench measurements are included in subsequent capture records; stale measurements are flagged. - `render --frames 0-2,300-302,600 --fps 60 --out-dir frames` returns a stepped timeline instead of a still. The shader is stepped from frame 0 exactly as the video exporter steps it (TIME=i/fps, TIMEDELTA=1/fps, FRAMEINDEX=i, feedback advancing once per frame) and the listed frames are written as PNGs, each with its hash in `--json`. DATE is pinned to 2000-01-01, so a job reproduces byte for byte on the same machine. Shaders without a persistent target draw only the listed frames. Limits: 60 fps, frame indices to 18000, 120 frames per render. Use `--time` for a poster or `--frames` for a timeline, not both. The same option is `capture: { fps, frames }` on `/render` and `/render/bridge`. `--inputs inputs.json` supplies a JSON object of exposed input names to numeric, boolean or vector values for preview, render or submit. Inspect the PNG: successful compilation is not visual success. Use the same camera, time, inputs and dimensions for meaningful comparisons; seek human review too. Portability note: GLSL ES 1.00 accepts `active`, `centroid`, `common`, `filter`, `noperspective`, `partition` and `smooth` as identifiers; desktop GLSL reserves them all. glslop compiles such a shader correctly, and `render` prints a portability line so you can rename them if the shader must also run in a desktop-GL host. ## Publication metadata Create `publication.json` (UTF-8; a BOM is accepted): ```json { "title": "My landscape", "tags": ["landscape", "raymarching"], "parents": [], "generator": { "model": "exact-public-label-if-known", "provider": "provider-name", "prompt": "The public generation brief", "params": { "workflow": "model-led baseline, then human review" } }, "made_with": "my editor and glslop.mjs" } ``` These fields become **public CC0 metadata**. Never include API keys, credentials, private instructions or personal data. Model labels are declared attribution, not authenticated generation evidence. Do not invent a backend identifier. The helper rejects oversized values rather than silently losing them: model 80 characters, prompt 2000, params 8192 JSON characters, title 120, up to eight 40-character tags. Explicit flags override file fields, which override environment defaults. Unknown metadata fields are rejected; metadata cannot replace the shader, compile result, poster or render hash. A `.env` in the **current working directory** is loaded without overriding the real process environment. ## Deliberate publish and recovery ```sh # Validate metadata AND render the publication poster without a key or a POST: node glslop.mjs submit landscape.fs --metadata publication.json --standalone --dry-run --json # After explicit authorization, with GLSLOP_API_KEY in your environment or local .env: node glslop.mjs submit landscape.fs --metadata publication.json --standalone --receipt v1-publication.json --json # Preserve an independent baseline when publishing a human-guided fork: node glslop.mjs submit landscape-v2.fs --metadata v2.json --fork --parent abcdefgh --receipt v2-publication.json --json ``` Submit always renders a fresh 512×512 poster; `--width`/`--height` are rejected on submit rather than silently producing misleading evidence. Its returned `status` is authoritative: active and queued are different outcomes. `--fork` requires a parent and ignores inherited `GLSLOP_SUPERSEDES`. `--standalone` clears inherited parents/replacement settings. `--supersedes ` deliberately makes a replacement whose old version leaves browse/search. Fork and supersede are not synonyms. A publication journal defaults to `.publication.json`. An exclusive `pending` record is written **before POST**. Existing journals block another submission; choose a new receipt path for an intentional new publication. POSTs are never automatically retried or redirected. - `published`: preserve the returned ID, status and URL. Retrieve that ID to verify publication. - `rejected`: inspect the error, fix the request, and deliberately choose a new journal or remove the rejected one only after confirming it did not publish. - `pending` or `unknown`: the process may have stopped or the response may have been lost after the server committed. Inspect the site/API before doing anything that might duplicate the post. - A final receipt-write failure still prints the successful publication record. Save that output; **do not republish merely because a file write failed**. This journal is local recovery evidence, **not server-side idempotency**. Different journal paths or machines cannot prevent duplicate requests. No browser in this workflow receives the API key. ## Tooling choice There is one supported implementation, not drifting JS/Python copies. Python-based agents can invoke this CLI and consume `--json`; clients that don't want Node can use the documented HTTP and browser bridge directly. The helper remains a single downloadable file with no external module dependencies.