Files
blog/index.html
T

230 lines
12 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog — Tinqs Studio</title>
<meta name="description" content="Dev logs, behind-the-scenes, and lessons learned from building games, tools, and platform infrastructure at Tinqs Studio.">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://www.tinqs.com/blog/">
<meta property="og:type" content="website">
<meta property="og:url" content="https://www.tinqs.com/blog/">
<meta property="og:title" content="Blog — Tinqs Studio">
<meta property="og:description" content="Dev logs, behind-the-scenes, and lessons learned from building games, tools, and platform at Tinqs Studio.">
<meta property="og:image" content="https://www.tinqs.com/img/og-cover.jpg">
<!-- PostHog (EU) -->
<script>
!function(t,e){var o,n,p,r;e.__SV||(window.posthog=e,e._i=[],e.init=function(i,s,a){function g(t,e){var o=e.split(".");2==o.length&&(t=t[o[0]],e=o[1]),t[e]=function(){t.push([e].concat(Array.prototype.slice.call(arguments,0)))}}(p=t.createElement("script")).type="text/javascript",p.crossOrigin="anonymous",p.async=!0,p.src=s.api_host.replace(".i.posthog.com","-assets.i.posthog.com")+"/static/array.js",(r=t.getElementsByTagName("script")[0]).parentNode.insertBefore(p,r);var u=e;for(void 0!==a?u=e[a]=[]:a="posthog",u.people=u.people||[],u.toString=function(t){var e="posthog";return"posthog"!==a&&(e+="."+a),t||(e+=" (stub)"),e},u.people.toString=function(){return u.toString(1)+".people (stub)"},o="init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys renderSurvey canRenderSurvey getNextSurveyStep identify setPersonProperties group resetGroups setPersonPropertiesForFlags resetPersonPropertiesForFlags setGroupPropertiesForFlags resetGroupPropertiesForFlags reset get_distinct_id getGroups get_session_id get_session_replay_url alias set_config startSessionRecording stopSessionRecording sessionRecordingStarted captureException loadToolbar get_property getSessionProperty createPersonProfile opt_in_capturing opt_out_capturing has_opted_in_capturing has_opted_out_capturing clear_opt_in_out_capturing debug".split(" "),n=0;n<o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||[]);
posthog.init('phc_teG6p5oxf6poQHPThq5AGKzWQNhw4bHW9arLwWAVXm3f',{api_host:'https://eu.i.posthog.com',ui_host:'https://eu.posthog.com',person_profiles:'identified_only',defaults:'2026-01-30'})
</script>
<link rel="icon" type="image/svg+xml" href="/img/favicon.svg">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../style.css">
2026-06-02 22:50:20 +01:00
<style>
/* ── Team guide aesthetic: self-contained overrides ── */
/* ── Section label kicker ── */
.section-label {
display: inline-block;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, monospace;
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: #38bdf8;
border: 1px solid rgba(147, 140, 129, 0.25);
border-radius: 999px;
padding: 4px 14px;
margin-bottom: 16px;
}
/* ── Gradient index title ── */
.blog-header__title {
background: linear-gradient(90deg, #c9935a, #f59e0b 40%, #38bdf8);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: 800;
}
/* ── Date pill ── */
.blog-card__date {
display: inline-block;
font-family: ui-monospace, 'SF Mono', 'Cascadia Code', Consolas, monospace;
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: #38bdf8;
border: 1px solid rgba(147, 140, 129, 0.25);
border-radius: 999px;
padding: 4px 14px;
margin-bottom: 10px;
}
/* ── Card hover accent ── */
.blog-card:hover {
border-color: #c9935a;
}
/* ── Read link accent ── */
.blog-card__read {
color: #38bdf8;
}
.blog-card:hover .blog-card__read {
color: #a855f7;
}
</style>
</head>
<body>
<!-- NAV -->
<nav class="nav nav--scrolled" id="nav">
<a href="/" class="nav__logo" aria-label="Tinqs home">
<span class="nav__wordmark">TINQS</span>
</a>
<div class="nav__links">
<a href="/#game" class="nav__link">Games</a>
<a href="/#tech" class="nav__link">Technology</a>
<a href="/#about" class="nav__link">About</a>
<a href="/blog/" class="nav__link" style="color: var(--c-accent-l);">Blog</a>
<a href="/#signup" class="nav__link">Contact</a>
<a href="/press" class="nav__link">Press</a>
</div>
<button class="nav__burger" aria-label="Open menu" id="navBurger">
<span></span><span></span><span></span>
</button>
</nav>
<!-- MOBILE MENU -->
<div class="mobile-menu" id="mobileMenu">
<a href="/#game" class="mobile-menu__link">Games</a>
<a href="/#tech" class="mobile-menu__link">Technology</a>
<a href="/#about" class="mobile-menu__link">About</a>
<a href="/blog/" class="mobile-menu__link">Blog</a>
<a href="/#signup" class="mobile-menu__link">Contact</a>
<a href="/press" class="mobile-menu__link">Press</a>
</div>
<!-- BLOG HEADER -->
<div class="blog-header">
<span class="section-label">Dev Log</span>
<h1 class="blog-header__title">From the Workshop</h1>
<p class="blog-header__subtitle">Behind-the-scenes notes on building games, forging tools, and running a small studio that punches above its weight.</p>
</div>
<!-- BLOG LIST -->
<div class="blog-list">
<!-- hand-authored HTML posts (not from build.js) -->
<a href="pi-flow-native-brain" class="blog-card">
<span class="blog-card__date">3 June 2026</span>
<h2 class="blog-card__title">How Pi Agents Build, Test, and Ship Game Code with Oracle-Backed Flows</h2>
<p class="blog-card__excerpt">When we ask Pi to build a game feature, it doesn't just write code. It compiles, runs tests, drives the live game, measures feel, fixes CI failures, and ships a green PR — all through composable oracle-backed flows.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="blog-visual-upgrade" class="blog-card">
<span class="blog-card__date">3 June 2026</span>
<h2 class="blog-card__title">How We Restyled Our Blog with Two Template Files and Zero Dependencies</h2>
<p class="blog-card__excerpt">We gave the Tinqs blog a visual refresh — gradient titles, dark code panels, date pills, amber accent bars. Two template files, one build step, zero external dependencies.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="cloud-harness" class="blog-card">
<span class="blog-card__date">26 May 2026</span>
<h2 class="blog-card__title">Agents That Code Overnight: Our $0.80 Cloud Harness with DeepSeek V4 and Pi</h2>
<p class="blog-card__excerpt">Every coding agent today runs in your terminal. Close the laptop, it stops. We built a cloud harness where agents code overnight for about $0.80 — and you can watch from a browser dashboard.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="agent-harness" class="blog-card">
<span class="blog-card__date">25 May 2026</span>
<h2 class="blog-card__title">What an Agent Harness Is and Why Game Dev Needs One</h2>
<p class="blog-card__excerpt">A raw AI model is stateless. An agent harness wraps around it and provides identity, memory, tools, context, and guardrails. Here's why game development needs its own.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="fal-image-generation" class="blog-card">
<span class="blog-card__date">25 May 2026</span>
<h2 class="blog-card__title">AI Art at Every Price Point: How We Generate Game Assets with fal.ai</h2>
<p class="blog-card__excerpt">We generate all visual assets for our game through fal.ai — concept art, icons, logos, trailer frames. Here's the 4-layer prompt pattern that actually works, and how we pick between 12 models spanning two orders of magnitude in cost.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="fork-dont-build" class="blog-card">
<span class="blog-card__date">25 May 2026</span>
<h2 class="blog-card__title">Fork, Don't Build: How We Modified Gitea, Pi, and Godot Instead of Starting from Scratch</h2>
<p class="blog-card__excerpt">Three forks, less than 0.5% code changed. Why modifying existing platforms beats building new ones — and how we turned Gitea into a game dev platform with 3D preview, AI agents, and LFS-first workflows.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="pre-commit-agent" class="blog-card">
<span class="blog-card__date">25 May 2026</span>
<h2 class="blog-card__title">A Pre-Commit Agent That Guards Your Secrets for $0.001</h2>
<p class="blog-card__excerpt">Too many things to remember before hitting commit. Don't leak API keys. Don't reference classified codenames. Don't link to deleted repos. We built a two-layer pre-commit hook — regex + LLM — that catches all of it for $0.001.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="godot-optimisation" class="blog-card">
<span class="blog-card__date">22 May 2026</span>
<h2 class="blog-card__title">Streaming a 12km Archipelago in Godot 4</h2>
<p class="blog-card__excerpt">Godot has no built-in asset streaming. We built four layers to run a 12km archipelago with 9 islands, 155 vegetation types, and 2,000 crowd instances — on an RTX 3060.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="studio-cli" class="blog-card">
<span class="blog-card__date">18 May 2026</span>
<h2 class="blog-card__title">One Binary to Rule Them All: Our Studio CLI</h2>
<p class="blog-card__excerpt">Every machine in our studio runs the same Go binary. It knows who you are, what machine you're on, and what services are reachable. It takes screenshots, sends them to cloud vision, and runs health checks — in 100ms.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
<a href="agentic-workflow" class="blog-card">
<span class="blog-card__date">6 March 2026</span>
<h2 class="blog-card__title">How a 4-Person Studio Runs on AI Agents</h2>
<p class="blog-card__excerpt">We gave AI agents persistent identities, skill playbooks, and access to our entire knowledge base. Here's how four people ship like forty.</p>
<span class="blog-card__read">Read &rarr;</span>
</a>
</div>
<!-- FOOTER -->
<footer class="footer">
<div class="footer__inner">
<span class="footer__wordmark">TINQS</span>
<div class="footer__links">
<a href="/#game">Games</a>
<a href="/#tech">Technology</a>
<a href="/#about">About</a>
<a href="/blog/">Blog</a>
<a href="mailto:hello@tinqs.com">hello@tinqs.com</a>
<a href="/press">Press Kit</a>
</div>
<p class="footer__copy">Tinqs Limited &mdash; London, est. 2020</p>
</div>
</footer>
<script>
const burger = document.getElementById('navBurger');
const mobileMenu = document.getElementById('mobileMenu');
burger.addEventListener('click', () => {
const open = mobileMenu.classList.toggle('mobile-menu--open');
burger.classList.toggle('nav__burger--open', open);
document.body.style.overflow = open ? 'hidden' : '';
});
mobileMenu.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.remove('mobile-menu--open');
burger.classList.remove('nav__burger--open');
document.body.style.overflow = '';
});
});
</script>
</body>
</html>