

Game UI toolkit for server-rendered browser games
Tooltips, timers, inventory grids, sound, modals, and a DOM-diffing fetch engine — everything a game UI needs, nothing it doesn't.
No framework. No build step. No virtual DOM. Just <script> tags and data attributes.
Frameworks ship hundreds of kilobytes of runtime, require build pipelines, and force you to manage state for what should be simple UI updates. Server-rendered games deserve leaner tools.
The heavy way
<!-- With a framework -->
<!-- 1. Install 200KB+ of runtime
2. Set up a build pipeline
3. Manage component state
4. Wire up reactivity
5. Debug hydration mismatches
6. Ship a bundle to the client -->
<!-- All to update a number on screen. -->The thrifty way
<!-- With Thryft -->
<div data-fetch="/api/resources"
data-interval="30000"
data-swap="inner">
<span class="gold">1,234</span>
<span class="wood">567</span>
</div>
<!-- "1,234" → "1,235" patches one text node.
No build step. No state management.
Just HTML and a 4KB script. -->~20KB
Full bundle
6
Standalone modules
0
Dependencies
0
Build step required
Load individually or use thryft.js for the combined bundle. Each module works standalone.
DOM-diffing HTML swap engine. Fetches server-rendered HTML and patches only what changed — no flicker, no animation interruption.
<!-- Poll every 30s, diff changes into DOM -->
<div data-fetch="/api/resources"
data-interval="30000"
data-swap="inner">
...server-rendered content...
</div>
<!-- Load once on page load -->
<div data-fetch="/api/activity"
data-trigger="load"
data-swap="inner">
</div>
<!-- Click to load into a target -->
<button data-fetch="/api/inventory"
data-target="#modal"
data-swap="inner">
Open Inventory
</button>Viewport-aware tooltip positioning. Anchored above/below triggers or following the cursor. Dynamic content via callbacks.
<!-- Anchored tooltip -->
<span class="has-tooltip">
Iron Sword
<div class="tooltip-box">
<strong>Iron Sword</strong>
<p>A sturdy blade. +12 Attack.</p>
</div>
</span>
<!-- Cursor-following with dynamic fill -->
<span data-tooltip="ship-tip"
data-tooltip-fill="fillShipInfo">
HMS Victory
</span>
<div id="ship-tip" class="tooltip-box">...</div>Countdown timers, progress bar fills, and resource interpolation. Ticks values up smoothly between server polls.
<!-- Countdown timer -->
<span data-timer
data-completes-at="2026-08-21T14:30:00Z">
</span>
<!-- Auto-reload + sound on completion -->
<span data-timer
data-completes-at="..."
data-reload-on-complete
data-sound="bell">
</span>
<!-- Progress bar fill -->
<div data-timer-fill
data-started-at="..."
data-completes-at="...">
</div>
<!-- Resource ticking between polls -->
<div data-timer-resource
data-current="1234" data-rate="50"
data-cap="5000">
<span data-timer-resource-value></span>
</div>Audio preloading on first interaction, playback API, and localStorage mute toggle. Integrates with timer.js completions.
// Register sounds once
Thryft.sound.register({
bell: { src: '/audio/bell.mp3', vol: 0.4 },
fanfare: { src: '/audio/fanfare.mp3', vol: 0.5 },
cannon: { src: '/audio/cannon.mp3', vol: 0.6 },
});
// Play
Thryft.sound.play('bell');
// Mute toggle (backed by localStorage)
Thryft.sound.toggle();
Thryft.sound.isEnabled();Lightbox/overlay system with stacking, Escape to close, click-outside dismiss, and scroll lock. Fetch content from server.
<!-- Fetch + open -->
<button data-modal-fetch="/api/news"
data-modal-target="#modal">
News
</button>
<!-- Toggle existing content -->
<button data-modal-toggle="#settings">
Settings
</button>
<!-- Close button -->
<button data-modal-close>×</button>
<!-- Modal structure -->
<div id="modal" class="thryft-modal">
<div class="thryft-modal-content">
...
</div>
</div>Drag-to-reorder grid with right-click context menu, touch support, and server-side reorder persistence.
<div data-inventory
data-reorder-url="/api/inventory/reorder">
<div class="inv-cell"
data-item-key="iron_ore"
data-name="Iron Ore"
data-quantity="24"
data-max-stack="99"
data-actions="track,open"
draggable="true">
<img src="/icons/iron_ore.png">
</div>
<div class="inv-cell"></div>
</div>
<script>
document.addEventListener(
'thryft:inventory-action', (e) => {
// { action: 'open', key: 'chest', cell }
});
</script>npm
CDN
<script src="https://unpkg.com/thryft/dist/thryft.min.js"></script>Or just download and self-host
<script src="/js/thryft.js"></script>
<!-- Or load only what you need -->
<script src="/js/thryft/fetch.js"></script>
<script src="/js/thryft/timer.js"></script>
<script src="/js/thryft/sound.js"></script>Extracted from patterns that proved themselves in production.
Naval idle MMO. Server-rendered with Express + EJS. Uses all six modules.
dunmast.com
Dwarf mining idle game. Same server-rendered stack. Uses fetch, tooltip, timer, inventory.
In development
Part of the Loxley Games open source toolkit