Download for macOS, Windows, and Linux →
A local file explorer for your whole computer. Browse any directory in the
browser, preview files, and author your own interactive views: any .html
file you open gets a tiny injected runtime that can call a Python main()
function and sync its state to the URL.
Runs entirely on 127.0.0.1. No accounts, no cloud, no sandboxing — your own
machine, your own trusted code. See SPEC.md / ARCHITECTURE.md / DECISIONS.md
for the full design.
Right-click a file in Explorer → Open with → fused-render, and it opens in your browser. See Windows: Explorer "Open with" to enable it.
macOS app — the packaged FusedRender.app (bundles the fused CLI and
rclone; no Python required):
brew install --cask fusedio/tap/fused-render
or download the DMG from the releases page.
Python package — each release also attaches a wheel (see the release
notes for its URL): pip install <wheel-url>. From a source checkout:
pip install -e .
Requires Python 3.10+. Installs FastAPI, uvicorn, and pyarrow (used by the built-in parquet preview).
Building from source and the local dev loop live in CONTRIBUTING.md.
fused-render
Opens a browser tab at http://127.0.0.1:1777/, starting in your home
directory. Useful flags:
fused-render --start-dir ~/data --port 9000 --no-browser
--start-dir only sets the initial location — the whole filesystem stays
browsable from there.
fused-render-open --register
Registers fused-render into Explorer's right-click "Open with" menu (HKCU
only, no admin) for every format it previews — double-clicking a file, or
picking "fused-render" from Open With, reuses a running server or starts one
detached, then opens the file. fused-render-open --unregister
removes the associations.
Any .py file is a runnable target as long as it defines a main()
function:
# sine.py
import math
def main(n: int = 80, freq: float = 1.0):
return {"points": [[i / n, math.sin(2 * math.pi * freq * i / n)] for i in range(n)]}Any .html file can call it and bind the result to the URL:
<input id="freq" type="range" min="0.1" max="5" step="0.1" />
<script>
const slider = document.getElementById("freq");
slider.addEventListener("input", () => fused.params.set("freq", slider.value));
fused.params.onChange(draw);
async function draw() {
const freq = fused.params.get("freq") || "1.0";
const { points } = await fused.runPython("./sine.py", { freq });
// ...render points...
}
draw();
</script>-
fused.runPython(pyPath, params)— runsmain(**params)of a local.pyfile in a fresh subprocess and returns its JSON result.pyPathmay be relative (to the HTML file) or absolute. -
fused.params— a string-only key/value store synced into the browser URL (get,getAll,set,onChange). Refreshing or bookmarking a view reproduces its exact state. -
fused.ai(prompt, opts?)— ask an AI model through theclaude(Claude Code) CLI on your machine; resolves with{text, model, usage}. Pass a Hugging Face repo id asmodel("Qwen/Qwen3.5-4B") and the same call runs a model locally instead — the slash is what tells them apart. Local chat works on every supported desktop platform: MLX is preferred on Apple Silicon, and everywhere else the default is a small CPU-only PyTorch build that runs on any machine — CUDA and ROCm builds exist and are one radio away on the AI Models page's Engines tab, offered only where the app can see a matching GPU. The AI Models page suggests models that suit whichever backend is serving you. Local calls also takehistory(prior{role, content}turns, for a conversation rather than one question) and can be stopped mid-answer withfused.ai.cancel(). -
fused.ai.image({prompt, ...})— text to image, locally; resolves with the PNG's path, a ready-made URL to point an<img>at, and the seed used (one is chosen for you if you don't pass one, so a render is always repeatable). It runs for minutes, soonProgressfires per denoising step — each tick with apreviewUrlfor a thumbnail of the image so far, so there is a picture emerging rather than a number going up — and the download manager's ✕ really stops it. -
fused.ai.transcribe({path, ...})— speech to text, locally: point it at an audio or video file on this machine and it resolves with the words plus the{start, end, text}segments that carry their timestamps.taskpicks between transcribing in the original language and translating to English; the language is auto-detected unless you name one. It runs for minutes, soonProgressfires with seconds of audio and the download manager's ✕ really stops it, and the transcript is written to a file so it outlives the tab. -
fused.ai.models.list() / load(id) / unload(id)— what this machine is holding in memory and what it costs. See the AI Models page (docs). -
fused.trackJob(spec)— report work your page is doing that runs longer than the page itself (exporting a few thousand tiles, converting a folder) to the download manager in the bottom-right corner, so it stays visible after you browse away:const job = fused.trackJob({ title: "Export tiles", kind: "export", unit: "items", cancellable: true }); job.update({ done: 1200, total: 4096, detail: "zoom 12" }); if (job.cancelRequested) stopTheWork(); // the manager's ✕ asked job.finish("Exported"); // or .fail(err) / .cancelled()
Reporting never throws and never blocks: a failed report cannot break the work it describes. Your page is the only thing that can stop its own work, so the ✕ here is a request you honour by checking
cancelRequested. -
fused.watchJob(id)— the other half: watch work the server is doing (fused.ai.image(),fused.ai.transcribe()andfused.ai.models.load()all hand you an id) with.onUpdate(cb),.get()and a.cancel()that really stops it — the server owns those processes, so its ✕ is an act rather than a request.
Built-in preview templates (parquet tables, images, text/code files) are
themselves just HTML files built on these same two primitives — open
fused_render/templates/ to see how.
Runtime features and settings live in docs/usage.md:
- Execution engine — built-in subprocess
runner vs. the
fusedlocal compute backend (folder-levelpyproject.tomldependencies in cached venvs), andFUSED_RENDER_ENGINE. - Remote storage (mounts) — mount S3-compatible stores, Google Drive, and anything else rclone speaks, as local folders.
- AI Models — what the Hugging Face cache holds on this machine, what it costs on disk, how to clear it, and a search of the Hub that says which results you already have, beside a short list of suggested models you can download.
- Preferences — the in-app settings panel (execution engine, logs, template registry).
- Export for hosted serving — the
programmatic
POST /api/exportbundle format for publishing a page yourself.
This repo doubles as a Claude Code plugin
marketplace. Installing the plugin adds skills that teach Claude how to use a fused-render
project (running the explorer, opening views by URL), author fused-render
views (the fused.runPython bridge, URL-synced params, file IO helpers), and
build custom preview templates.
From inside Claude Code:
/plugin marketplace add fusedio/fused-render
/plugin install fused-render@fused-render
Or from the command line:
claude plugin marketplace add fusedio/fused-render
claude plugin install fused-render@fused-render
The manifests live in .claude-plugin/ (marketplace.json +
plugin.json); the skills themselves are under skills/.
Installing is optional and only affects your own Claude Code sessions. Chats
started from inside fused-render (the Claude and split-view templates, and app
scaffolding) already get these skills: the app assembles the same plugin under
~/.fused-render/skill-plugin/ and loads it per session with --plugin-dir,
so they work on a plain wheel or DMG install with nothing cloned.
