Skip to content
fpsr
Esc
navigateopen⌘Jpreview
On this page

Viewer

Blueprint inspection GUI at fpsr.fprints.xyz.

The viewer is a React app for pasting, inspecting, previewing, and exporting Factorio blueprint strings. It consumes the workspace fpsr package and private CDN assets.

Live demo: https://fpsr.fprints.xyz

Local development from the monorepo root:

pnpm dev
# or: vp dev apps/viewer

Docs for the library live under Guide; this site is fpsr-docs.fprints.xyz.

Load from URL (?source=)

Open the viewer with a source query parameter pointing at an HTTPS URL that returns a Factorio blueprint string (text/plain). The viewer fetches it through a same-origin proxy, adds it under Custom, selects it, and strips source from the address bar so a refresh does not re-import.

https://fpsr.fprints.xyz/?source=https://www.factorio.school/api/blueprintData/<id>/

Rules:

  • HTTPS only (no credentials in the URL)
  • Allowlisted hosts: www.factorio.school, factorio.school
  • Upstream body capped at 4 MiB; request times out after 15s

The proxy is GET /api/fetch-blueprint?url=… (Cloudflare Pages Function in production; Vite middleware in local pnpm dev). It is not an open proxy — non-allowlisted hosts are rejected.

For large blueprint strings in third-party pages, prefer Embed with postMessage instead of putting the string in the query.

Embed

Third-party sites can iframe a chrome-stripped preview and pass a blueprint string with postMessage (do not put large blueprint strings in the URL).

Live embedLoading viewer…
<iframe
  id="fpsr"
  src="https://fpsr.fprints.xyz/?embed=1"
  title="FPSR blueprint preview"
  style="width:100%;height:480px;border:0"
></iframe>
const iframe = document.getElementById("fpsr");

window.addEventListener("message", (event) => {
  if (event.source !== iframe.contentWindow) return;
  const data = event.data;
  if (!data || typeof data !== "object") return;

  if (data.type === "fpsr:ready") {
    iframe.contentWindow.postMessage(
      { type: "fpsr:load", version: 1, blueprint: "0eN..." },
      "https://fpsr.fprints.xyz",
    );
  }
  if (data.type === "fpsr:loaded") {
    console.log("loaded", data.kind);
  }
  if (data.type === "fpsr:error") {
    console.error(data.message);
  }
});
Direction Type Payload
iframe → parent fpsr:ready { version: 1 }
parent → iframe fpsr:load { version: 1, blueprint: string }
iframe → parent fpsr:loaded { version: 1, kind }
iframe → parent fpsr:error { version: 1, message: string }

Game sprites still load from BunnyCDN under the viewer origin. Hashed atlases cache in the browser and at the edge; each iframe still boots its own app shell and render worker (in-memory atlas cache is not shared across iframes).

Was this page helpful?