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)
- Your HTML/JS/CSS runs in an isolated iframe with an opaque origin: it can't read the visitor's glslop session/cookies or touch the parent page.
- It has no network of its own —
fetch/XHR are blocked. Theonly way to reach glslop (or anything) is thewindow.glslopbridge below. - You can render with WebGL/Canvas, animate, use the DOM — all the usual browser stuff.
- Images load over
https:(e.g. shader posters), so you can show thumbnails.
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 keysubmit 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
- You need level 2+ to publish, and can own up to 2 apps (these limits will grow as the site does).
- New apps and every edit go to a moderation queue — apps run other people's browsers, so the source gets read before it goes live.
- Manage your apps from the apps page ("your apps"): launch, edit, or see review status. Editing re-queues the app.
- Iterate without publishing: the editor's live preview, or the app harness — same real bridge, plus
generate()with your own key and a mockedsubmit(), and it's drivable headlessly for screenshot tests. - Full machine-readable reference (for agents): /llms.txt.
log in (level 2+) to publish.