glslop
[ CC0 shader vibe commons · everything here is public domain ]
hosted apps · guide

build an app

A glslop app is one self-contained HTML file. It runs sandboxed in a visitor's browser and talks to glslop through a small window.glslop bridge — that's the whole API. No build step, no server, no framework required.

the sandbox (what your app can and can't do)

the window.glslop bridge

// reads (open)
await glslop.listShaders({ tag?, q?, limit? })  // -> [{ id, title, poster, tags, author_name, author_url }]
await glslop.search(q)                            // -> same shape
await glslop.getShader(id)                        // -> full shader incl. .isf

// show shaders live (injected renderer — no key, no round-trip)
glslop.render(canvasOrSelector, isf, { time? })   // -> { ok, errors, stop() }  live preview
await glslop.snapshot(isf, { width?, height?, time? }) // -> { ok, dataUrl }  one frame (thumbnails)

// write (asks the visitor for consent; posts the shader as them)
await glslop.submit(isf, { title? })              // -> { id, url, status }

// generate with the visitor's own LLM key (you never see it) -> raw text + token usage
await glslop.generate({ prompt, reference?, model?, system? }) // -> { text, usage:{ in, out } }
glslop.extractShader(text)                        // -> ISF string (or '' if none) — local, no key

submit renders the canonical poster on glslop's real engine and posts under the visitor's account (provenance tool) — it goes to the same moderation queue as any shader. generate is brokered by the page using the visitor's own API key (Anthropic / OpenAI / Gemini / OpenRouter / Ollama / any custom endpoint), stored only in their browser. It returns the model's raw text — a sensible ISF system prompt is the default, but pass your own system (or '') to use generation however you like; then call glslop.extractShader() if you want a shader out of it. You don't declare capabilities — glslop reads them out of your code (read/submit/generate) and shows visitors what your app actually does before they launch it.

a complete app

Roll a looping shader, preview it live, publish the one you like. Paste this into the editor:

<!doctype html>
<html><head><meta charset="utf-8"><style>
  body{margin:0;font-family:monospace;background:#111;color:#eee}
  #cv{width:100%;height:70vh;display:block;background:#000}
  .bar{padding:10px;display:flex;gap:8px;align-items:center}
  button{font:inherit;padding:6px 10px;cursor:pointer}
</style></head>
<body>
  <canvas id="cv"></canvas>
  <div class="bar">
    <button id="roll">roll</button>
    <button id="pub">publish this</button>
    <span id="msg"></span>
  </div>
  <script>
    var cur = '';
    function roll(){
      var h = Math.random().toFixed(3);
      cur = '/*{ "DESCRIPTION":"hue wash", "INPUTS":[] }*/\n'
          + 'void main(){ vec2 uv = isf_FragNormCoord;\n'
          + '  vec3 c = 0.5 + 0.5*cos(TIME + uv.xyx*3.0 + vec3(0.0,2.0,4.0) + ' + h + '*6.28);\n'
          + '  gl_FragColor = vec4(c, 1.0); }';
      glslop.render('#cv', cur);            // live preview, injected for you
    }
    document.getElementById('roll').onclick = roll;
    document.getElementById('pub').onclick = async function(){
      try { var r = await glslop.submit(cur, { title: 'hue wash' });   // asks the visitor first
            document.getElementById('msg').textContent = 'published #' + r.id; }
      catch(e){ document.getElementById('msg').textContent = e.message; }
    };
    roll();
  </script>
</body></html>

publishing

log in (level 2+) to publish.