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
importTimelineJSONsiproject.timelineouproject.keyframesprésent. - Flip vertical automatique des pixels WebGL (
readPixelsorigin bas-gauche → PNG origin haut-gauche). - Logger de progression en barres
█░— notifie Tauri viaheadless_logIPC. headless_exitIPC appelé en fin de run (code 0) ou sur erreur (code 1).printHeadlessHelp()affichée pour--help,-hou commande inconnue.
src/native/headless-cli.test.js— 33 tests Vitest.src/app/init.js(modifié) — importrunHeadlessCLI+ early-exit si--headless renderdé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.mp4Files
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étectebestCompressedFormatviagl-caps.js, ouvre IndexedDBz-gl-tex-cache.compressAndCacheTexture(blob, name)— charge l'image dans unOffscreenCanvas, 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::formatchange (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é) — importcompressAndCacheTexture+getActiveFormat;_chLoadCompressedTexture(): uploadgl.compressedTexImage2Davec le bon enum WebGL par format, wrappé enTHREE.Texture; fallback Three.js siresult.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.jsexistant (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.createDiffEditorinline-mode avec thèmevs-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égrationscoreGLSL, 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é :scoreGLSLdepuis../shader/glsl-complexity.js. Fonctions ajoutées :_switchMode,_buildModePrompt,_showDiff,_showTextDiff,_hideDiff,_collectSelection,_handleApplyDiff,_handleDiscardDiff,_applyToEditor. Logique_handleGenerateunifié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.js—mpSetPassResScale: clampage descale=0retournait1au lieu de0.125(Number(0) || 1→isNaNguard).src/shader/hlsl-transpiler.js—hlslToGlslStructural: le semantic: SV_Targetn'était pas supprimé des signatures de retour avant{.src/shader/glsl-to-wgsl.test.js— TestsiDate/iSampleRate/iChannelTime/iChannelResolutionmis à jour : ces uniforms sont désormais dans le struct_SLUniforms(plus deconst). TestiFrame: regex flexible pour l'alignement.src/plugins/phase11.test.js—await import()à l'intérieur de blocsdescribe()remplacé par desimportstatiques (incompatible Rollup/Vitest).src/ai/local-model.test.js—require()CommonJS remplacé parawait import()ESM pour accéder au mocksafeLocalSet.
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 viaFloat32Array→ImageData(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 dansPOST_FRAG:hable(),agx(),reinhardExt(),lottes(). Dispatch étendu de 2 à 7 modes dansapplyToneMapper.src/ui.html(modifié) — 5 nouvelles<option>dans#toneMapSel(Filmic Hable, AGX, Reinhard étendu, Lottes) + bouton#hdrToggleBtndans la barre d'outils.src/ui/editor.js(modifié) — lazy-loadhdr-ui.js, exportsopenHDRPanel,closeHDRPanel,toggleHDRPanel, raccourciCtrl+Shift+E, entrée Command Palette.src/ui/events.js(modifié) — importtoggleHDRPanel+ 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 enTEXTURE_3DWebGL2 (unit 6) ou en atlas 2D automatique si WebGL2 indisponible (tuilesceil(sqrt(d)) × ceil(d/cols)).bindVolumeUniforms(gl, prog)injecteiVolume,iVolumeAtlas,iVolumeAtlasCols,iVolumeSizeW/H/Ddans le programme actif.destroyVolumeRenderer()libère les textures GL.initVolumeRenderer()→ API publique.src/render/transfer-function.js—TransferFunctionEditor: 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 (uniformiTransferFn). CRUD :addPoint,setPoint,removePoint,reset. CallbackonChange.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 parImagePositionPatient) ;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 » (snippetcom:volume-renderer), bouton Reset TF, bouton Vider volume.src/shader/snippet-library.js— Snippetcom:volume-renderer: template GLSL complet ray marching avec caméra orbitale, intersection AABB, boucle 128 steps, transfer function, fond dégradé. Tagsvolume,ray-marching,iVolume,15.2,webgl2,dicom,vdb.src/ui/editor.js—toggleVolumeRenderer()exporté, lazy-loadvolume-ui.js, raccourciCtrl+Shift+U, entrée Command Palette « Toggle Volume Renderer ».src/ui/events.js— ImporttoggleVolumeRenderer, bindingvolumeRendererToggleBtn.src/ui.html— Bouton « volume » dans la barre d'outils principale (icône cylindre SVG).src/core/state.js—state.volumeinitialisé parvolume-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. UniformiSamplesexposé. 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 communautairecom: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.js—togglePathTracer()exporté, raccourciCtrl+Shift+T, entrée Command Palette "Toggle Path Tracer WebGPU".src/core/state.js—state.wgpu.pathTracerinitialisé à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 dossiermodels\à côté de l'exe (portable). Retourne nom, chemin absolu, taille Mo de chaque.onnxtrouvé. 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.js—scanLocalModelFiles()appellescan_local_modelset retourne des entrées compatiblesMODEL_CATALOGavecsource:'local'.loadModel()patché : sisource === 'local', charge viaconvertFileSrc(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.onnxdu 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
- Télécharger
model_quantized.onnxdepuis HuggingFace (une seule fois, sur n'importe quelle machine). - Copier le fichier dans
%APPDATA%\z-gl\models\(oumodels\à côté de l'exe). - Ouvrir Z-GL → panneau AI → le modèle apparaît automatiquement → cliquer Utiliser.
- 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é localement —
ort.min.js(436 Ko),ort.webgpu.min.js(326 Ko),ort-wasm-simd-threaded.wasm(11 Mo) etort-wasm-simd-threaded.jsep.wasm(21 Mo) copiés danspublic/onnxruntime/. Plus aucun appel CDN jsdelivr. llm-worker.js—_loadOnnxRuntime()charge maintenant depuis/onnxruntime/viaimportScripts(localUrl)+ort.env.wasm.wasmPaths = localBase. Fallback message d'erreur clair si le dossier est absent.vite.config.js— plugincopyOnnxRuntime()ajouté : copiepublic/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 alerting —
setPassBudget(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 GPU —
estimateOccupancy(): 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()etexportSessionSVG()+ 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.jssi 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.
- 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
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.uniformswith 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,vec4via Three.js.x/.y/.z/.wproperty inspection. - CSV Logger — right-click any row → Start CSV Logger; every frame appends
timestamp_ms, frame, valuein 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 fromstate.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-resizenear the divider. - Visual diff mode — switch to
⊕ Diffto renderabs(A − B) × ampper pixel. Two sub-modes: Raw RGB (direct amplified difference) and Heatmap (blue→cyan→green→yellow→red gradient viasmoothstep). 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
⚠ Errorbadge with the full log as tooltip. - Zero new dependencies — pure WebGL2 (
ShaderPass,ABCompositorclasses); 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-parseralready 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 shortcuts —
Ctrl+Shift+D+ Command Palette entry. - Zero new dependencies — uses the
glsl-transpiler.jsruntime (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 AudioAnalyserNode— RMS silence guard, first-peak search, 16-frame median smoothing. Auto-wired intochSetupAudio()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:17523WebSocket bridge. Live peer count display. Fully offline (LAN only, zero internet).
Changed
src/timeline/beat-sync.js— complete rewrite (479 lines); exportsSNAP_DIVISIONS,setSnapDivision,getSnapDivision,setMidiClockInEnabled,setMidiClockOutEnabled,toggleMidiClockOut,sendMidiClockStart,sendMidiClockStop,initBpmDetect,startBpmDetect,stopBpmDetect,setLinkEnabled,toggleLink.src/channels/channels-audio.js—chSetupAudio()callsinitBpmDetect()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, version1.0.0). Ctrl+Shift+Qshortcut + 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+Aor 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+Ashortcut + 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, version1.0.0). Ctrl+Shift+Nshortcut.- 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 towindow.__TAURI__.*(requireswithGlobalTauri: trueintauri.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-parserusesnew Function()internally. Added'unsafe-eval'toscript-srcCSP intauri.conf.json. - Build error —
tauri.conf.jsonUTF-8 BOM: PowerShellConvertTo-Json/Set-Contentwrites BOM; Tauri's JSON parser rejects it.build.ps1now uses[System.IO.File]::WriteAllTextwith 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).
.zglproject 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
rgba16floatHDR targets. - WASM-based
glslangpre-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-queryGPU timing in the perf panel. - Single full-screen triangle draw call (replaces Three.js
PlaneGeometry). SharedArrayBuffer+Atomicsuniform writes — zero GC pressure.
[1.0.0] — initial release
- Real-time GLSL shader editor in the browser.
- ShaderToy-compatible
mainImageconvention. - Standard uniforms:
iResolution,iTime,iTimeDelta,iFrame,iMouse,iChannel0–3. - Auto-extracted
#define/const floatsliders 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 (
postMessageAPI +window['Z-GL']).