National Porting Association (NPA) — client library for embedding and serving NPA ported games on your website.
Docs: https://npa.lol/docs
This repo contains a small client bundle and a build script (build.py
) used to produce a single dist/bundle.js
that exposes runtime helpers on window.Builder
and embeds fallback assets on window.__embeddedData
. Use the client to fetch the NPA catalog, show metadata, and embed playable ports into your pages — with automatic fallback to embedded data when the network is unavailable.
- Quick overview
- Features
- Installation
- Quick start (HTML)
- Use client (React)
- Programmatic usage (JS)
- Build system (
build.py
) - Manifest & embedded data
- Contributing
- Where to find docs & support
- License
The bundle provides:
window.Builder
— runtime helpers for fetching catalogs and creating embed UI.window.__embeddedData
— JSON and small assets embedded at build time for offline fallback.window.Play
— play overlay and mini-player helpers.- A small Python build script (
build.py
) that concatenates JavaScript files and embeds JSON/images into the bundle. - Learn more about what this bundle provides at https://npa.lol/docs.
The API is for adding and serving NPA ported games so other sites can embed those ports and let visitors play them in-place.
- Single-file bundle (
dist/bundle.js
) — easy to include on any static site. - Embedded assets fallback — bundle contains JSON/images so your site can still show games if the API is unreachable.
- Minimal runtime API (
window.Builder.*
,window.Play.*
) for fetching catalogs and mounting players. - Small dev UI with quick actions (inspect flags, preview embedded assets, open mini-player).
- Simple build pipeline (Python) to generate the bundle and
dist/manifest.json
.
Script tag Include the built bundle on any page:
<script>
// optional: set flags before the bundle initializes
window.__builderFlags = { hideDevButton: true, env: 'prod' };
</script>
<script src="/path/to/dist/bundle.js" data-flags="env=dev"></script>
This example loads the bundle, fetches the games catalog, and inserts a playable iframe for the first game.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>NPA API Play Demo</title>
<!-- Inline flags -->
<script>
window.__builderFlags = {
hideDevButton: false,
env: 'dev'
};
</script>
<!-- Load built bundle -->
<script src="bundle.js"></script>
<style>
body {
font-family: system-ui, sans-serif;
margin: 0;
padding: 0;
background: #121212;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
color: #eee;
}
.container {
background: #1e1e1e;
padding: 1rem;
width: 100%;
max-width: 400px;
}
header {
margin-bottom: 1.5rem;
text-align: center;
}
header h1 {
font-size: 1.4rem;
margin: 0 0 0.25rem;
color: #fff;
}
header p {
margin: 0;
font-size: 0.9rem;
color: #aaa;
}
.controls {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-bottom: 1rem;
}
button {
padding: 0.6rem 1rem;
border: none;
border-radius: 8px;
background: #000000;
color: #fff;
font-size: 0.95rem;
cursor: pointer;
transition: background 0.2s ease;
}
button:hover {
background: #131313;
}
.output {
background: #111;
border: 1px solid #2a2a2a;
border-radius: 8px;
padding: 0.75rem;
font-family: monospace;
font-size: 0.85rem;
min-height: 60px;
white-space: pre-wrap;
color: rgb(82, 81, 81);
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>NPA API Demo</h1>
<p>Play overlay & mini-player test</p>
</header>
<main class="controls">
<button id="overlayById">Open Overlay (ID "1")</button>
<button id="miniById">Open Mini-player (ID "1")</button>
</main>
<div class="output" id="output">Logs will appear here...</div>
</div>
<script>
const log = msg => {
const out = document.getElementById('output');
out.textContent += msg + "\n";
out.scrollTop = out.scrollHeight;
};
document.getElementById('overlayById').addEventListener('click', () => {
if (window.Play) {
window.Play.open('1');
log("Opened overlay by ID = '1'");
} else {
log("window.Play not available (bundle not loaded?)");
}
});
document.getElementById('miniById').addEventListener('click', () => {
if (window.Builder) {
window.Builder.openMiniPlayer('1');
log("Opened mini-player by ID = '1'");
} else {
log("window.Builder not available (bundle not loaded?)");
}
});
</script>
</body>
</html>
Examples of common tasks using the window.Builder
helpers that the bundle provides.
// logs the game list to the console
window.Builder.fetchGames().then(games => console.log(games)).catch(err => console.error(err))
const embeddedCatalog = window.__embeddedData && window.__embeddedData['data/games.json'];
window.Play.open('1')
window.Play.open(id)
— opens the fullscreen overlay for the given play id.window.Builder.openMiniPlayer(id)
— open a mini-player for the given play id.
A small Python script (build.py
) concatenates .js
files and embeds .json
, .txt
, and small images into the bundle.
Usage
python build.py
# outputs: dist/bundle.js and dist/manifest.json
files.txt
files.txt
lists each source file (relative paths). Each.js
in that list gets appended to the final bundle in order..json
and.txt
files listed are parsed and embedded underwindow.__embeddedData['path/to/file.json']
.- small images (
.png
,.jpg
,.jpeg
,.ico
) are embedded as data URLs and assigned intowindow.__embeddedData
(andwindow.__builderIconDataUrl
in the special*icon.png
case).
Output
dist/bundle.js
— single JS file that sets embedded data then appends all JS parts.dist/manifest.json
— lists files included and which keys were embedded.
Key behaviour
- The build script will ensure
window.__embeddedData
exists and assign each embedded asset with its normalized path as the key. - If a key ends with
icon.png
, the script setswindow.__builderIconDataUrl
to that data URL (convenience for UIs).
dist/manifest.json
contains:
{
"files": [ "lib/part1.js", "data/games.json", "assets/icon.png" ],
"embedded_keys": [ "data/games.json", "assets/icon.png" ],
"bundle": "bundle.js"
}
At runtime:
window.__embeddedData['data/games.json']
contains the parsed JSON content embedded during the build.window.__builderIconDataUrl
(if present) contains a base64data:
URL for a small icon.
This lets the client code attempt network fetches first, and fall back to window.__embeddedData
when the fetch fails.
Contributions are welcome.
- Open issues on the repository with clear reproduction steps.
- For code changes, follow these steps:
- Create a branch (
git checkout -b feat/your-feature
). - Edit source files and
files.txt
as needed. - Run
python build.py
and verifydist/bundle.js
behaves as expected. - Submit a PR with an explanation and screenshots where appropriate.
- Create a branch (
If you add large binary assets, consider tracking them with Git LFS.
Full API and field-level documentation: https://npa.lol/docs
For issues, feature requests, or support: open an issue in this repo, or join our Discord server at https://discord.com/invite/SsW6agAQxR.
MIT © National Porting Association