Skip to content

Z-GL 1.2.0 — The Premier Shader Desktop Environment

Latest

Choose a tag to compare

@Patrickjaillet Patrickjaillet released this 21 May 21:19
9cfae9e

Changelog

All notable changes to Z-GL are documented here.
Format follows Keep a Changelog.

[1.2.0] — 20 mai 2026

Added — Mode headless CLI (Tauri)

  • src/native/headless-cli.js — moteur headless complet (~430 lignes).
    • z-gl render --shader main.glsl --duration 10 --fps 60 --out output.mp4 — rendu offscreen Three.js sans fenêtre UI, encodage MP4/H.264 via WebCodecs (VideoEncoder).
    • z-gl render --timeline project.json --out frames/ — export séquence PNG depuis un projet Z-GL JSON avec timeline embarquée.
    • z-gl render --shader plasma.frag --out seq/ — export PNG séquence (détection automatique par / terminal ou absence d'extension).
    • Parser d'arguments minimaliste (--key value, flags booléens, positionnels).
    • Tous les defaults : --duration 10, --fps 60, --width 1920, --height 1080, --bitrate 8 (Mbit/s), --start-time 0.
    • Encodage H.264 (avc1.42E01E) ou séquence PNG, en fonction du suffixe --out.
    • Import de timeline via importTimelineJSON si project.timeline ou project.keyframes présent.
    • Flip vertical automatique des pixels WebGL (readPixels origin bas-gauche → PNG origin haut-gauche).
    • Logger de progression en barres █░ — notifie Tauri via headless_log IPC.
    • headless_exit IPC appelé en fin de run (code 0) ou sur erreur (code 1).
    • printHeadlessHelp() affichée pour --help, -h ou commande inconnue.
  • src/native/headless-cli.test.js — 33 tests Vitest.
  • src/app/init.js (modifié) — import runHeadlessCLI + early-exit si --headless render détecté dans les argv Tauri, sans monter l'UI.

Exemples d'utilisation

# MP4 10 s à 60 fps, 1920×1080
z-gl render --shader main.glsl --duration 10 --fps 60 --out output.mp4

# Séquence PNG 4K 30 fps
z-gl render --shader plasma.frag --width 3840 --height 2160 --fps 30 --out frames/

# Export depuis une timeline de projet
z-gl render --timeline project.json --out export.mp4

# Utilisable dans un Makefile
render: shaders/main.glsl
	z-gl render --shader $< --duration 5 --fps 60 --out dist/preview.mp4

Files

src/native/headless-cli.js (~430 lignes), src/native/headless-cli.test.js (~280 lignes).


[Unreleased] — 20 mai 2026

Added — Textures compressées natives (BC7 / ASTC / ETC2)

  • src/render/texture-compressor.js — moteur de compression/cache (~630 lignes).
    • initTextureCompressor(gl) — détecte bestCompressedFormat via gl-caps.js, ouvre IndexedDB z-gl-tex-cache.
    • compressAndCacheTexture(blob, name) — charge l'image dans un OffscreenCanvas, calcule le SHA-256 du bitmap, consulte le cache IDB, compresse si absent (blocs 4×4 CPU), stocke le résultat.
    • loadCachedTexture(key) — lecture IDB directe depuis une clé connue.
    • getActiveFormat() — retourne 'bc7'|'astc'|'etc2'|'none' selon le GPU détecté.
    • purgeTextureCache() — vide le store IDB entier.
    • getTextureCacheStats(){ entries, totalBytes, format }.
    • Invalidation automatique si vendor::renderer::format change (fingerprint GPU).
    • Dégradation silencieuse si aucun format compressé : { compressed: false, url } → Three.js charge l'image normalement.
  • src/render/texture-compressor.test.js — tests Vitest (~377 lignes) : init, compression BC7/ASTC/ETC2, hit/miss cache, stats, purge, passthrough sans GPU.
  • src/channels/channels-media.js (modifié) — import compressAndCacheTexture + getActiveFormat ; _chLoadCompressedTexture() : upload gl.compressedTexImage2D avec le bon enum WebGL par format, wrappé en THREE.Texture ; fallback Three.js si result.compressed === false.

Formats GPU supportés

Format Extension WebGL Plateformes
BC7 EXT_texture_compression_bptc Windows / Linux — AMD, NVIDIA, Intel
ASTC WEBGL_compressed_texture_astc Apple Silicon, Mali, Adreno (mobile)
ETC2 WEBGL_compressed_texture_etc Android, Chrome OS

Files

src/render/texture-compressor.js (~630 lignes), src/render/texture-compressor.test.js (~377 lignes).


[Unreleased] — 20 mai 2026

Added

  • Phase 13.5 — Image locale → Shader procédural
    • Drag & drop d'une image PNG/JPEG/WEBP/GIF → analyse locale complète (palette, luminance, fréquences spatiales) → shader GLSL procédural paramétrique généré par le LLM local.
    • Zéro donnée envoyée : tout tourne en canvas 2D + LLM Worker existant, aucune image ne quitte la machine.
    • Palette extraite par Median-Cut (6 couleurs dominantes) — algorithme utilisé par les éditeurs photo professionnels.
    • Analyse de luminance : mean, RMS contrast, gradient moyen, répartition dark/mid/bright, flags high-contrast/dark/bright.
    • Analyse de fréquences spatiales via DCT 8×8 blocs → 4 bandes → détection grain, smoothness, motifs répétitifs.
    • Panneau UI avec prévisualisation, palette inline, métriques, génération streamée, Apply/Copy/Cancel.
    • Raccourci Ctrl+Shift+I · bouton « Img→Shader » dans la barre d'outils · entrée Command Palette.
    • 38 tests Vitest (38/38 ✓).

Added

  • Phase 13.3 — Commandes en langage naturel (panneau local)
    • Panneau AI Copilot refactorisé avec 4 onglets : Generate, Explain, Optimize, Refactor.
    • Mode Generate : prompt libre → patch GLSL/WGSL généré, prévisualisé en diff Monaco inline (éditeur diff côte-à-côte dans le panneau) avant application. Boutons ✓ Apply / ✕ Discard / Copy.
    • Mode Explain : utilise la sélection Monaco courante (ou tout le fichier si rien de sélectionné) → prose technique avec référence explicite aux uniforms Z-GL actifs (iTime, iResolution, iMouse, etc.). Aucun code en sortie.
    • Mode Optimize : analyse glsl-complexity.js existant (score ALU, branches, texture fetches, loops) → badge de complexité en temps réel dans le panneau → prompt structuré avec score intégré → suggestion de réécriture complète du shader prévisualisée en diff Monaco.
    • Mode Refactor : instruction en langage naturel (ex : "extrais cette boucle en fonction nommée fbmNoise") → shader refactorisé complet prévisualisé en diff Monaco. Interdit de renommer les uniforms Z-GL injectés.
    • Diff Monaco : monaco.editor.createDiffEditor inline-mode avec thème vs-dark, lecture seule, sans minimap. Fallback en diff texte coloré (+/-) si Monaco non disponible.
    • src/ai/ai-copilot-phase13-3.test.js — 40 tests Vitest couvrant les 4 modes : construction de prompts Explain/Optimize/Refactor, intégration scoreGLSL, extraction de blocs de code fencés, dialectes WGSL/HLSL.

Changed

  • src/ai/ai-copilot-ui.js — refactorisé de ~555 à ~851 lignes. Largeur panneau élargie à 380px pour le diff. Import ajouté : scoreGLSL depuis ../shader/glsl-complexity.js. Fonctions ajoutées : _switchMode, _buildModePrompt, _showDiff, _showTextDiff, _hideDiff, _collectSelection, _handleApplyDiff, _handleDiscardDiff, _applyToEditor. Logique _handleGenerate unifiée pour tous les modes.

[Unreleased] — Tests · 20 mai 2026

Added

  • src/shader/parser-extended.test.js — 90 nouveaux tests unitaires pour le parser GLSL : parseShader (defines, const globals, littéraux inline, directives preprocesseur), parseSymbols (fonctions, variables, structs, surcharges, tableaux, fallback regex), typeCheck (arité builtins, fonctions utilisateur, entiers passés à float fns), inferRange, categorize, buildLabel.

Fixed

  • src/render/multipass.jsmpSetPassResScale : clampage de scale=0 retournait 1 au lieu de 0.125 (Number(0) || 1isNaN guard).
  • src/shader/hlsl-transpiler.jshlslToGlslStructural : le semantic : SV_Target n'était pas supprimé des signatures de retour avant {.
  • src/shader/glsl-to-wgsl.test.js — Tests iDate/iSampleRate/iChannelTime/iChannelResolution mis à jour : ces uniforms sont désormais dans le struct _SLUniforms (plus de const). Test iFrame : regex flexible pour l'alignement.
  • src/plugins/phase11.test.jsawait import() à l'intérieur de blocs describe() remplacé par des import statiques (incompatible Rollup/Vitest).
  • src/ai/local-model.test.jsrequire() CommonJS remplacé par await import() ESM pour accéder au mock safeLocalSet.

Stats

  • 779 tests passent (18 fichiers) — 0 échec.

Added

  • src/render/hdr.js — module HDR complet : détection canvas HDR Tauri (Display-P3), activation/désactivation, 7 tone mappers GLSL (None, Reinhard, ACES Filmic, Filmic Hable, AGX, Reinhard étendu, Lottes), export 16-bit PNG via Float32ArrayImageData (fallback natif navigateur), export OpenEXR pur JS avec fallback tinyexr-wasm.
  • src/render/hdr-ui.js — panneau UI draggable HDR : toggle canvas HDR (Tauri), sélecteur de tone mapper, boutons export 16-bit PNG / OpenEXR avec sélection des passes RGBA.
  • src/render/perf.js (modifié) — 5 tone mappers supplémentaires dans POST_FRAG : hable(), agx(), reinhardExt(), lottes(). Dispatch étendu de 2 à 7 modes dans applyToneMapper.
  • src/ui.html (modifié) — 5 nouvelles <option> dans #toneMapSel (Filmic Hable, AGX, Reinhard étendu, Lottes) + bouton #hdrToggleBtn dans la barre d'outils.
  • src/ui/editor.js (modifié) — lazy-load hdr-ui.js, exports openHDRPanel, closeHDRPanel, toggleHDRPanel, raccourci Ctrl+Shift+E, entrée Command Palette.
  • src/ui/events.js (modifié) — import toggleHDRPanel + binding #hdrToggleBtn.

[1.1.19] — 2026-05-20

Added — Volume Rendering (iVolume, VDB, DICOM, Transfer Function)

  • src/render/volume-renderer.js — Moteur volume : uploadVolume(data, w, h, d) charge les données voxel en TEXTURE_3D WebGL2 (unit 6) ou en atlas 2D automatique si WebGL2 indisponible (tuiles ceil(sqrt(d)) × ceil(d/cols)). bindVolumeUniforms(gl, prog) injecte iVolume, iVolumeAtlas, iVolumeAtlasCols, iVolumeSizeW/H/D dans le programme actif. destroyVolumeRenderer() libère les textures GL. initVolumeRenderer() → API publique.
  • src/render/transfer-function.jsTransferFunctionEditor : 5 points de contrôle par défaut (densité → RGBA), interpolation smooth-step cubique entre points, getLUT()Float32Array[256×4], getTexture(gl) → texture WebGL 256×1 RGBA8 (uniform iTransferFn). CRUD : addPoint, setPoint, removePoint, reset. Callback onChange.
  • src/render/volume-importer.js — Trois importeurs 100 % locaux : importVDB(arrayBuffer) via bridge WASM openvdb (/wasm/openvdb.js) ; importDICOMFiles(fileList) — parser natif JS P10 Explicit VR Little Endian (tags DICOM, rescale slope/intercept, windowing auto, tri Z par ImagePositionPatient) ; importRaw(ab, w, h, d, fmt) pour uint8/uint16/float32.
  • src/render/volume-ui.js — Panneau UI draggable : drop zone VDB/DICOM/RAW, canvas interactif Transfer Function (clic = ajouter point, drag = déplacer, clic-droit = supprimer, remplissage couleur + courbe d'opacité), sliders ray march (max steps 16–512, max dist, density scale, step size), badge statut WebGL2/atlas, bouton « Insérer template » (snippet com:volume-renderer), bouton Reset TF, bouton Vider volume.
  • src/shader/snippet-library.js — Snippet com:volume-renderer : template GLSL complet ray marching avec caméra orbitale, intersection AABB, boucle 128 steps, transfer function, fond dégradé. Tags volume, ray-marching, iVolume, 15.2, webgl2, dicom, vdb.
  • src/ui/editor.jstoggleVolumeRenderer() exporté, lazy-load volume-ui.js, raccourci Ctrl+Shift+U, entrée Command Palette « Toggle Volume Renderer ».
  • src/ui/events.js — Import toggleVolumeRenderer, binding volumeRendererToggleBtn.
  • src/ui.html — Bouton « volume » dans la barre d'outils principale (icône cylindre SVG).
  • src/core/state.jsstate.volume initialisé par volume-renderer.js : { enabled, w, h, d, useWebGL2, atlasCols, maxSteps, maxDist, densityScale, stepSize, tfDirty }.

[1.1.18] — 2026-05-20

Added — Path Tracer WebGPU temps réel

  • src/render/path-tracer.js — Pipeline WebGPU compute complet : kernel de path tracing (1–4 spp, GGX BRDF + importance sampling, réflexions diffuses/spéculaires, ombres douces, émission, SSS wrap) avec accumulation progressive rgba32float. Débruiteur A-Trous 5 passes ping-pong en WGSL. Blit final ACES + sRGB. Uniform iSamples exposé. API : initPathTracer, dispatchPathTracer, resetPathTracerAccumulation, setPathTracerSPP, setPathTracerBounces, setPathTracerExposure, setPathTracerDenoiser, getPathTracerSamples, resizePathTracer, destroyPathTracer.
  • src/render/path-tracer-ui.js — Panneau flottant draggable : sliders SPP (1–4), rebonds (1–8), exposition, toggle débruiteur, statut live (samples accumulés), boutons activer/reset/stop.
  • src/shader/snippet-library.js — Snippet communautaire com:path-tracer : template WGSL complet (scène de base, caméra orbitale, diffus cosine hemisphere, lumière sphérique) prêt à personnaliser.
  • src/ui/editor.jstogglePathTracer() exporté, raccourci Ctrl+Shift+T, entrée Command Palette "Toggle Path Tracer WebGPU".
  • src/core/state.jsstate.wgpu.pathTracer initialisé à null (lazy-init par path-tracer.js).

[1.1.17] — 2026-05-19

Added — AI Copilot : modèle local sans téléchargement

  • Commande Tauri scan_local_models — scanne %APPDATA%\z-gl\models\ et le dossier models\ à côté de l'exe (portable). Retourne nom, chemin absolu, taille Mo de chaque .onnx trouvé. Crée le dossier s'il n'existe pas.
  • Commande Tauri get_models_dir — retourne le chemin du dossier models (affiché dans l'UI pour guider l'utilisateur).
  • local-model.jsscanLocalModelFiles() appelle scan_local_models et retourne des entrées compatibles MODEL_CATALOG avec source:'local'. loadModel() patché : si source === 'local', charge via convertFileSrc(localPath) sans passer par IndexedDB ni aucun téléchargement.
  • ai-copilot-ui.js — section « Modèles locaux détectés » dans le panneau AI : liste les .onnx du dossier, bouton Utiliser par fichier, bouton ↻ Scanner, bouton 📂 Ouvrir (ouvre le dossier dans l'Explorateur). Scan automatique à l'ouverture du panneau. Auto-sélection du premier modèle local détecté.

Utilisation

  1. Télécharger model_quantized.onnx depuis HuggingFace (une seule fois, sur n'importe quelle machine).
  2. Copier le fichier dans %APPDATA%\z-gl\models\ (ou models\ à côté de l'exe).
  3. Ouvrir Z-GL → panneau AI → le modèle apparaît automatiquement → cliquer Utiliser.
  4. Zéro téléchargement, 100 % offline, chargement direct depuis le disque.

Files

Modifiés : src-tauri/src/lib.rs (+70 lignes), src/ai/local-model.js (+70 lignes), src/ai/ai-copilot-ui.js (+80 lignes).


[1.1.16] — 2026-05-19

Fixed — AI Copilot 100 % offline

  • onnxruntime-web embarqué localementort.min.js (436 Ko), ort.webgpu.min.js (326 Ko), ort-wasm-simd-threaded.wasm (11 Mo) et ort-wasm-simd-threaded.jsep.wasm (21 Mo) copiés dans public/onnxruntime/. Plus aucun appel CDN jsdelivr.
  • llm-worker.js_loadOnnxRuntime() charge maintenant depuis /onnxruntime/ via importScripts(localUrl) + ort.env.wasm.wasmPaths = localBase. Fallback message d'erreur clair si le dossier est absent.
  • vite.config.js — plugin copyOnnxRuntime() ajouté : copie public/onnxruntime/dist/onnxruntime/ au build Tauri.

Files

public/onnxruntime/ (nouveau dossier, 4 fichiers, ~33 Mo). Modifiés : src/ai/llm-worker.js, vite.config.js.


[1.1.15] — 2026-05-19

Added — GPU Profiler étendu

  • Flamegraph interactif par passe — SVG généré localement via buildFlamegraphSVG(sortBy), triable par durée ou par nom. Tooltip sur chaque barre, badge ⚠ rouge si la passe dépasse son budget configuré. Bouton « Export SVG » pour sauvegarder localement.
  • Budget alertingsetPassBudget(passId, ms) définit un seuil par passe. getOverBudgetPasses() retourne les passes hors-budget. Badges ⚠ passId Xms (+Y%) affichés en temps réel dans le panneau profiler. Champs de saisie ms par passe dans l'UI.
  • Estimations GPUestimateOccupancy() : ratio ms actives / budget frame (0–100 %). estimateBandwidth() : estimation GB/s basée sur textures actives × résolution × fps. Affichées et actualisées à ~7.5 Hz dans le panneau.
  • Historique de session — ring buffer Float32Array(18000) pour fps et ms par passe sur toute la session (~5 min @ 60 fps). Canvas 2D avec courbe FPS (vert), ligne budget 60 fps (jaune tirets), courbes de passes colorées. exportSessionCSV() et exportSessionSVG() + boutons de téléchargement local.

Files

src/render/gpu-profiler.js (enrichi, +377 lignes). Aucune dépendance externe ajoutée.


[Unreleased] — Heatmaps d'analyse — 2026-05-19

Added

  • Phase 14.4 — Heatmaps d'analyse : trois modes de visualisation thermique du coût de rendu, entièrement offline.
    • Instruction heatmap : émulation CPU pixel-par-pixel sur grille 64×64 ; chaque pixel est évalué via le transpileur GLSL existant avec hook de comptage d'instructions. Fallback sur projection statique des poids de glsl-heatmap-analysis.js si la transpilation échoue.
    • UV heatmap : instrumentation du shader transpilé pour capturer les coordonnées UV lors de chaque appel texture() ; histogramme 32×32 des accès → détection des zones sur-échantillonnées. Palette bleu/cyan/rose distinctive.
    • Branch heatmap : double-exécution du shader avec perturbation ε sur iTime ; le delta de comptage révèle les zones de divergence de flot de contrôle. Complété par une analyse statique des if/for/while/discard/ternaires. Palette violet→rose.
    • Panel UI flottant draggable (src/debug/glsl-heatmap-ui.js) : sélecteur de mode, mini-canvas 224×126, légende gradient, stats (grid, lignes, % hot/cold), bouton overlay semi-transparent sur le viewport, export PNG local.
    • Raccourci Ctrl+Shift+H, bouton « heatmap » dans la barre d'outils, entrée Command Palette.

Files

src/debug/glsl-heatmap-analysis.js (~260 lignes), src/debug/glsl-heatmap-ui.js (~320 lignes). Modifiés : src/debug/index.js (+6), src/ui/editor.js (+35), src/ui/events.js (+3), src/ui.html (+10).



[1.1.10] — 2026-05-19

Added — Uniform Watcher

  • Live Uniform Watcher panel — floating, resizable, draggable window listing all active state.mat3.uniforms with real-time values and a per-uniform mini-sparkline (64-sample ring buffer ≈ 1 s at 60 fps). Textures and samplers are automatically filtered out.
  • Sparkline — 120×28 px canvas per uniform; gradient fill + trailing dot. Colour-coded per uniform (10-colour palette).
  • Type detection — handles float, int, bool, vec2, vec3, vec4 via Three.js .x/.y/.z/.w property inspection.
  • CSV Logger — right-click any row → Start CSV Logger; every frame appends timestamp_ms, frame, value in memory. Stop & Download CSV triggers a Blob <a download> — no server, no file system API required.
  • Freeze / Thaw — snapshots the current uniform value (deep-clones vector components) and re-applies it each frame before the renderer reads it, keeping the simulation advancing while the uniform stays constant. Per-row ❄ button + global Freeze All / Thaw header buttons.
  • Live filter — instant text filter across all uniform names.
  • Zero new dependencies — vanilla JS, injected <style>, reads directly from state.mat3.uniforms.

Files added / modified

File Role
src/debug/uniform-watcher.js Complete module: Ring, CSVLogger, _drawSparkline, initUniformWatcher() factory, injected CSS (~420 lines)
src/debug/index.js +1 line: export { initUniformWatcher }

Integration

import { initUniformWatcher } from './debug/uniform-watcher.js';
const uw = initUniformWatcher();
document.getElementById('uniformWatcherBtn')?.addEventListener('click', uw.toggle);

[1.1.9] — 2026-05-19

Added — Shader A/B Comparator

  • Split-line viewport — two GLSL fragment shaders rendered simultaneously in the same WebGL2 canvas, separated by a draggable white hairline. Drag with mouse or touch, or use the Split slider. Cursor changes to col-resize near the divider.
  • Visual diff mode — switch to ⊕ Diff to render abs(A − B) × amp per pixel. Two sub-modes: Raw RGB (direct amplified difference) and Heatmap (blue→cyan→green→yellow→red gradient via smoothstep). Amplification slider ×1 to ×20.
  • PNG export — captures the current diff frame into an offscreen FBO, reads pixels, flips vertically (WebGL → PNG origin), annotates with amp/mode/resolution, and triggers a native <a download> — no server required.
  • Live compilation — each shader textarea recompiles 400 ms after the last keystroke. GLSL errors surface as a ⚠ Error badge with the full log as tooltip.
  • Zero new dependencies — pure WebGL2 (ShaderPass, ABCompositor classes); no Three.js, no external libs.

Files added

File Role
src/debug/shader-ab-comparator.js Complete module: ShaderPass (per-shader FBO), ABCompositor (split-pass + diff-pass GPU programs), initShaderABComparator() factory, injected CSS (~1 020 lines)

Integration

import { initShaderABComparator } from './debug/shader-ab-comparator.js';
const ab = initShaderABComparator({ vpCanvas: document.getElementById('glCanvas') });
ab.open();
ab.setShaderA(codeA);
ab.setShaderB(codeB);

[1.1.8] — 2026-05-19

Added — GLSL Step Debugger (CPU emulation)

  • Pick-pixel interface — click any pixel in the viewport → the fragment shader executes on CPU for that exact pixel via JS transpilation of the GLSL AST (@shaderfrog/glsl-parser already present via Phase 12/13).
  • Step controls — Step Over (F10), Step Into (F11), Step Out (Shift+F11), Step Back, Continue (F5), scrub with a step slider.
  • Variable panel — every variable captured at each execution step with its value; vec2/vec3/vec4 values rendered with inline colour swatches (background = the colour the vector represents).
  • Conditional breakpoints — set a line number + optional JS expression (fragColor.r > 0.9, uv.x < 0.5, etc.); execution pauses only when the condition is truthy. Click gutter to add/remove simple breakpoints.
  • Monaco integration — current execution line highlighted in the editor with revealLineInCenter; decorations cleared on close.
  • Keyboard shortcutsCtrl+Shift+D + Command Palette entry.
  • Zero new dependencies — uses the glsl-transpiler.js runtime (GVec2/3/4, 80+ built-in GLSL functions) already part of the codebase.

Files added / modified

File Role
src/debug/glsl-transpiler.js GLSL→JS runtime (copied from standalone transpiler)
src/debug/glsl-debugger.js Debug engine: CPU execution, variable capture, breakpoints, step navigation
src/debug/glsl-debugger-ui.js Side-panel UI: source view, variables, breakpoint manager, progress slider
src/debug/index.js Public entry point
src/ui/editor.js +35 lines: lazy-load, toggleGLSLDebugger(), Ctrl+Shift+D, palette entry
src/ui/events.js +3 lines: import + button binding
src/ui.html +8 lines: debug button in toolbar

[1.1.7] — 2026-05-19

Added — Advanced music synchronisation

  • Real-time BPM detection via FFT autocorrelation (_autocorrelationBpm()) on the Web Audio AnalyserNode — RMS silence guard, first-peak search, 16-frame median smoothing. Auto-wired into chSetupAudio() via dynamic import. Activated with the auto button in the beat-sync toolbar.
  • Tap Tempo — 8-tap sliding window with 2 s auto-reset.
  • Extended snap grid — 13 divisions across four families: straight (1/1, 1/2, 1/4, 1/8, 1/16, 1/32), triplets (1/3T, 1/6T, 1/12T), quintuplets (1/5Q, 1/10Q), septuplets (1/7S, 1/14S). Dropdown popup with grouped sections in the beat-sync toolbar.
  • MIDI Clock In — Z-GL follows an external DAW tempo; 24 PPQN, BPM smoothed over 24 tick samples, timeline position scrubbed in real-time.
  • MIDI Clock Out — Z-GL sends a 24 PPQN clock (0xF8) to all open MIDI outputs via requestAnimationFrame. Start (0xFA) / Stop (0xFC) sent automatically on toggle.
  • Ableton Link — tempo sync with other apps on the same LAN via ws://localhost:17523 WebSocket bridge. Live peer count display. Fully offline (LAN only, zero internet).

Changed

  • src/timeline/beat-sync.js — complete rewrite (479 lines); exports SNAP_DIVISIONS, setSnapDivision, getSnapDivision, setMidiClockInEnabled, setMidiClockOutEnabled, toggleMidiClockOut, sendMidiClockStart, sendMidiClockStop, initBpmDetect, startBpmDetect, stopBpmDetect, setLinkEnabled, toggleLink.
  • src/channels/channels-audio.jschSetupAudio() calls initBpmDetect() after analyser setup.
  • src/ui/events.js — new imports and dispatch entries for all 16.2 actions; snap popup open/close logic.
  • src/ui.html — beat-sync toolbar extended: snap dropdown (13 entries), auto BPM button, MIDI In/Out buttons, Link button + peer count.
  • src/style/timeline.css — added .tl-snap-div, .tl-snap-group, .tl-snap-popup, .beat-clock-status.err, .beat-clock-status.connecting.

[1.1.6] — 2026-05-19

Added — Pro multi-track sequencer

  • 6 track types: uniform_scalar, uniform_vector (per-component x/y/z/w), post_pass, channel, shader, event.
  • 3 event types: screenshot, reset_itime, change_resolution.
  • Arrangement view (long-form scroll + zoom) and Pattern view (short loop), switchable instantly without data loss.
  • Named section markers with custom colours — drag to reposition, right-click to rename.
  • Clips — double-click to create/delete, drag to move, drag edges to resize, fade-in/out visualised.
  • Mute / Solo per track.
  • Scrubable playhead via ruler or track area click.
  • Dedicated RAF playback engine — loop with configurable loopStart/loopEnd, per-frame uniform/post-pass/shader application.
  • JSON export/import (sequencer.json, version 1.0.0).
  • Ctrl+Shift+Q shortcut + toolbar button.

Files

src/timeline/sequencer.js (~290 lines), src/ui/sequencer-ui.js (~430 lines), src/style/sequencer.css (~140 lines).


[1.1.5] — 2026-05-19

Added — Advanced post-processing (14 passes)

All passes implemented in pure local GLSL — zero external libraries:

Pass Key parameters
SSR (Screen-Space Reflections) steps, thickness, fade
SSAO (HBAO+ simplified, 16 samples) radius, bias, strength
Motion Blur samples, scale
Depth of Field (hexagonal bokeh) focalDist, focalRange, bokehRadius
Chromatic Aberration radialStr, transverseStr
Lens Flare (100% procedural) threshold, ghosts, haloRadius, strength
Dithering (Bayer 2×2/4×4/8×8, Blue Noise) mode, scale
Halftone (dots, lines, crosses) mode, frequency, angle
Pixel Sort (luminance + mask) threshold, direction
Datamosh (procedural delta-frame artefacts) blockSize, amount
Glitch (RGB split, scanlines, noise) rgbSplit, scanlines, noise
Kuwahara (anisotropic paint filter) radius
Posterize levels
ASCII art (real-time glyph GLSL 0–9) charSize

Files

src/render/post-passes-advanced.js (~400 lines), src/render/perf.js extended.


[1.1.4] — 2026-05-19

Added — Inline AI completion

  • Ghost text in Monaco (Tab to accept) — powered by the local LLM worker.
  • Context-aware prompt: active uniforms, other passes' code, current compilation errors, GPU stats, active dialect (GLSL 1.00/3.00/4.60, WGSL, HLSL, ISF).
  • 600 ms debounce — zero impact on render frame rate.
  • Auto-stop after 4 lines or first double line-break.
  • Toggle via Ctrl+Shift+A or Command Palette.

Files

src/ai/inline-completion.js (~160 lines), updates to src/ai/local-model.js, src/ai/ai-copilot-ui.js, src/ai/index.js, src/ui/editor.js.


[1.1.3] — 2026-05-19

Added — Local AI copilot (embedded WASM/ONNX)

  • Integrated quantised LLM (Phi-3 Mini Q4 ~2 GB or Qwen2.5-Coder 1.5B Q8 ~1.5 GB) via ONNX Runtime Web (WebGPU or WASM backend).
  • Model downloaded once on first launch and cached in IndexedDB (z-gl-models). No re-download.
  • Runs in a dedicated Web Worker — never blocks the render thread.
  • Docked panel with progress bar, token streaming, "Apply in Monaco" button.
  • Ctrl+Shift+A shortcut + Command Palette entry.

Files

src/ai/llm-worker.js (~210 lines), src/ai/local-model.js (~230 lines), src/ai/ai-copilot-ui.js (~230 lines), src/ai/index.js (~15 lines), src/ai/local-model.test.js (~110 lines).


[1.1.2] — 2026-05-19

Added — Visual node graph ↔ code (bidirectional, first of its kind)

  • Node canvas — 100% custom 2D engine (zero external lib), draggable/zoomable nodes, double-click search panel.
  • Library of 82 nodes across 8 categories: Input, Math, Noise, SDF, Colour, Geometry, Logic, Output.
  • Typed connections (float, vec2, vec3, vec4, mat2, mat3, mat4, sampler2D, bool, int) with per-type wire colour and float→vecN coercion.
  • Per-node output preview on hover (inline mini-canvas).
  • Node → GLSL/WGSL compiler: topological sort (Kahn), CSE deduplication, readable annotated output.
  • GLSL → node decompiler: ~30 recognised patterns (built-ins, noise, SDF, colour). Unrecognised code → editable Raw Code nodes.
  • Bidirectional real-time hybrid mode: node edit → 600 ms debounce → compile → Monaco update; Monaco edit → 800 ms debounce → decompile → graph rebuild.
  • Resizable split view (nodes | Monaco | toolbar).
  • JSON export/import (zgl-node-graph.json, version 1.0.0).
  • Ctrl+Shift+N shortcut.
  • 33/33 Vitest tests passing.

Files

src/nodegraph/node-graph.js (~530 lines), src/nodegraph/node-library.js (~480 lines), src/nodegraph/node-compiler.js (~340 lines), src/nodegraph/node-decompiler.js (~240 lines), src/nodegraph/node-graph-ui.js (~430 lines), src/nodegraph/index.js (~20 lines), src/nodegraph/node-graph.test.js (~280 lines).


[1.1.1] — 2026-05-11

Fixed

  • Production exe — app fails to initialise: dynamic import('@tauri-apps/...') calls not bundled by Vite. All Tauri API calls migrated to window.__TAURI__.* (requires withGlobalTauri: true in tauri.conf.json). Affected: src/plugins/plugin-loader.js, src/plugins/plugin-manager-ui.js, src/midi/osc.js.
  • Production exe — GLSL parser EvalError: @shaderfrog/glsl-parser uses new Function() internally. Added 'unsafe-eval' to script-src CSP in tauri.conf.json.
  • Build error — tauri.conf.json UTF-8 BOM: PowerShell ConvertTo-Json/Set-Content writes BOM; Tauri's JSON parser rejects it. build.ps1 now uses [System.IO.File]::WriteAllText with no-BOM UTF8 encoding.

[1.1.0] — 2026-05-01

Added — Native Tauri desktop application

  • Frameless window with custom title bar (Windows 11 design).
  • Native Mica/Acrylic blur effects via window-vibrancy.
  • System tray: Open / Pause Render / Quit.
  • Native file dialogs; drag-and-drop for .glsl, .frag, .fs, .json.
  • File associations — open shaders from Windows Explorer.
  • CLI launch: z-gl.exe shader.glsl.
  • MRU file list (last 10 paths).
  • .zgl project format — ZIP bundle (shader + metadata + channels + presets).
  • Auto-save every 30 s with crash recovery.
  • Hot-reload on external file changes.
  • Fixed-ratio rendering presets (720p, 1080p, 1440p, 4K).
  • Headless screenshot export via OffscreenCanvas.
  • WebCodecs video export (MP4, WebM, PNG sequence).
  • Timeline keyframe editor with Bézier curve view and dope sheet.
  • Multi-pass WebGPU pipelines (Buffers A/B/C/D) with rgba16float HDR targets.
  • WASM-based glslang pre-validation before GPU execution.
  • WGSL real-time preview panel alongside GLSL source.
  • Monaco GLSL ES 3.0 language service — syntax highlighting, hover docs, autocomplete, live error markers.
  • WebGPU timestamp-query GPU timing in the perf panel.
  • Single full-screen triangle draw call (replaces Three.js PlaneGeometry).
  • SharedArrayBuffer + Atomics uniform writes — zero GC pressure.

[1.0.0] — initial release

  • Real-time GLSL shader editor in the browser.
  • ShaderToy-compatible mainImage convention.
  • Standard uniforms: iResolution, iTime, iTimeDelta, iFrame, iMouse, iChannel0–3.
  • Auto-extracted #define / const float sliders with undo/redo.
  • 4 channels: noise, image, video, webcam, mic, audio FFT.
  • Preset library with JSON import/export.
  • Share URL (compressed base64 hash).
  • Embed mode (postMessage API + window['Z-GL']).