10 interesting stories served every morning and every evening.
A project trust management system. People must be vouched for before interacting with certain parts of a project (the exact parts are configurable to the project to enforce). People can also be explicitly
denounced to block them from interacting with the project.
The implementation is generic and can be used by any project on any code forge, but we provide GitHub integration out of the box via GitHub actions and the CLI.
The vouch list is maintained in a single flat file using a minimal format that can be trivially parsed using standard POSIX tools and any programming language without external libraries.
Vouch lists can also form a web of trust. You can configure Vouch to read other project’s lists of vouched or denounced users. This way, projects with shared values can share their trust decisions with each other and create a larger, more comprehensive web of trust across the ecosystem. Users already proven to be trustworthy in one project can automatically be assumed trustworthy in another project, and so on.
Open source has always worked on a system of trust and verify.
Historically, the effort required to understand a codebase, implement a change, and submit that change for review was high enough that it naturally filtered out many low quality contributions from unqualified people. For over 20 years of my life, this was enough for my projects as well as enough for most others.
Unfortunately, the landscape has changed particularly with the advent of AI tools that allow people to trivially create plausible-looking but extremely low-quality contributions with little to no true understanding. Contributors can no longer be trusted based on the minimal barrier to entry to simply submit a change.
But, open source still works on trust! And every project has a definite group of trusted individuals (maintainers) and a larger group of probably trusted individuals (active members of the community in any form). So, let’s move to an explicit trust model where trusted individuals can vouch for others, and those vouched individuals can then contribute.
Who and how someone is vouched or denounced is left entirely up to the project integrating the system. Additionally, what consequences a vouched or denounced person has is also fully up to the project. Implement a policy that works for your project and community.
Integrating vouch into a GitHub project is easy with the
provided GitHub Actions. By choosing which actions to use, you can fully control how users are vouched and what they can or can’t do.
For an example, look at this repository! It fully integrates vouch.
Below is a list of the actions and a brief description of their function. See the linked README in the action directory for full usage details.
The CLI is implemented as a Nushell module and only requires Nushell to run. There are no other external dependencies.
This is Nushell, so you can get help on any command:
use vouch *
help add
help check
help denounce
help gh-check-pr
help gh-manage-by-issue
vouch check
# Preview new file contents (default)
vouch add someuser
# Write the file in-place
vouch add someuser –write
# Preview new file contents (default)
vouch denounce badactor
# With a reason
vouch denounce badactor –reason “Submitted AI slop”
# Write the file in-place
vouch denounce badactor –write
Requires the GITHUB_TOKEN environment variable. If not set and gh
is available, the token from gh auth token is used.
# Check PR author status (dry run)
vouch gh-check-pr 123 –repo owner/repo
# Auto-close unvouched PRs (dry run)
vouch gh-check-pr 123 –repo owner/repo –auto-close
# Actually close unvouched PRs
vouch gh-check-pr 123 –repo owner/repo –auto-close –dry-run=false
# Allow unvouched users, only block denounced
vouch gh-check-pr 123 –repo owner/repo –require-vouch=false –auto-close
# Dry run (default)
vouch gh-manage-by-issue 123 456789 –repo owner/repo
# Actually perform the action
vouch gh-manage-by-issue 123 456789 –repo owner/repo –dry-run=false
Responds to comments from collaborators with write access:
* vouch — vouches for the issue author with a reason
Keywords are customizable via –vouch-keyword and –denounce-keyword.
The module also exports a lib submodule for scripting:
use vouch/lib.nu *
let records = open VOUCHED.td
$records | check-user “mitchellh” –default-platform github # “vouched”, “denounced”, or “unknown”
$records | add-user “newuser” # returns updated table
$records | denounce-user “badactor” “reason” # returns updated table
$records | remove-user “olduser” # returns updated table
The vouch list is stored in a .td file. See
VOUCHED.example.td for an example. The file is looked up at VOUCHED.td or .github/VOUCHED.td by default.
* One handle per line (without @), sorted alphabetically.
* Optionally add details after a space following the handle.
The from td and to td commands are exported by the module, so Nushell’s open command works natively with .td files to decode into structured tables and encode back to the file format with comments and whitespace preserved.
...
Read the original on github.com »
You’re using AI to be more productive. So why are you more exhausted than ever? The paradox every engineer needs to confront.
You’re using AI to be more productive. So why are you more exhausted than ever? The paradox every engineer needs to confront.
I shipped more code last quarter than any quarter in my career. I also felt more drained than any quarter in my career. These two facts are not unrelated.
I build AI agent infrastructure for a living. I’m one of the core maintainers of OpenFGA (CNCF Incubating), I built agentic-authz for agent authorization, I built Distill for context deduplication, I shipped MCP servers. I’m not someone who dabbles with AI on the side. I’m deep in it. I build the tools that other engineers use to make AI agents work in production.
And yet, I hit a wall. The kind of exhaustion that no amount of tooling or workflow optimization could fix.
If you’re an engineer who uses AI daily - for design reviews, code generation, debugging, documentation, architecture decisions - and you’ve noticed that you’re somehow more tired than before AI existed, this post is for you. You’re not imagining it. You’re not weak. You’re experiencing something real that the industry is aggressively pretending doesn’t exist. And if someone who builds agent infrastructure full-time can burn out on AI, it can happen to anyone.
I want to talk about it honestly. Not the “AI is amazing and here’s my workflow” version. The real version. The one where you stare at your screen at 11pm, surrounded by AI-generated code you still need to review, wondering why the tool that was supposed to save you time has consumed your entire day.
Here’s the thing that broke my brain for a while: AI genuinely makes individual tasks faster. That’s not a lie. What used to take me 3 hours now takes 45 minutes. Drafting a design doc, scaffolding a new service, writing test cases, researching an unfamiliar API. All faster.
But my days got harder. Not easier. Harder.
The reason is simple once you see it, but it took me months to figure out. When each task takes less time, you don’t do fewer tasks. You do more tasks. Your capacity appears to expand, so the work expands to fill it. And then some. Your manager sees you shipping faster, so the expectations adjust. You see yourself shipping faster, so your own expectations adjust. The baseline moves.
Before AI, I might spend a full day on one design problem. I’d sketch on paper, think in the shower, go for a walk, come back with clarity. The pace was slow but the cognitive load was manageable. One problem. One day. Deep focus.
Now? I might touch six different problems in a day. Each one “only takes an hour with AI.” But context-switching between six problems is brutally expensive for the human brain. The AI doesn’t get tired between problems. I do.
This is the paradox: AI reduces the cost of production but increases the cost of coordination, review, and decision-making. And those costs fall entirely on the human.
Before AI, my job was: think about a problem, write code, test it, ship it. I was the creator. The maker. That’s what drew most of us to engineering in the first place - the act of building.
After AI, my job increasingly became: prompt, wait, read output, evaluate output, decide if output is correct, decide if output is safe, decide if output matches the architecture, fix the parts that don’t, re-prompt, repeat. I became a reviewer. A judge. A quality inspector on an assembly line that never stops.
This is a fundamentally different kind of work. Creating is energizing. Reviewing is draining. There’s research on this - the psychological difference between generative tasks and evaluative tasks. Generative work gives you flow states. Evaluative work gives you decision fatigue.
I noticed it first during a week where I was using AI heavily for a new microservice. By Wednesday, I couldn’t make simple decisions anymore. What should this function be named? I didn’t care. Where should this config live? I didn’t care. My brain was full. Not from writing code - from judging code. Hundreds of small judgments, all day, every day.
The cruel irony is that AI-generated code requires more careful review than human-written code. When a colleague writes code, I know their patterns, their strengths, their blind spots. I can skim the parts I trust and focus on the parts I don’t. With AI, every line is suspect. The code looks confident. It compiles. It might even pass tests. But it could be subtly wrong in ways that only surface in production, under load, at 3am.
So you read every line. And reading code you didn’t write, that was generated by a system that doesn’t understand your codebase’s history or your team’s conventions, is exhausting work.
This is also why I think agent security and authorization matter so much. If we can’t review everything AI produces - and we can’t, not at scale - then we need systems that constrain what agents can do in the first place. Least-privilege access, scoped tokens, audit trails. The less you have to worry about “did the AI do something dangerous,” the more cognitive budget you have for the work that actually matters. This isn’t just a security problem. It’s a human sustainability problem.
Engineers are trained on determinism. Same input, same output. That’s the contract. That’s what makes debugging possible. That’s what makes reasoning about systems possible.
I had a prompt that worked perfectly on Monday. Generated clean, well-structured code for an API endpoint. I used the same prompt on Tuesday for a similar endpoint. The output was structurally different, used a different error handling pattern, and introduced a dependency I didn’t ask for.
Why? No reason. Or rather, no reason I can access. There’s no stack trace for “the model decided to go a different direction today.” There’s no log that says “temperature sampling chose path B instead of path A.” It just… happened differently.
For someone whose entire career is built on “if it broke, I can find out why,” this is deeply unsettling. Not in a dramatic way. In a slow, grinding, background-anxiety way. You can never fully trust the output. You can never fully relax. Every interaction requires vigilance.
I tried to fight this. I version-controlled my prompts. I built elaborate system messages. I created templates. Some of it helped. None of it solved the fundamental problem: you are collaborating with a probabilistic system, and your brain is wired for deterministic ones. That mismatch is a constant, low-grade source of stress.
This frustration is actually what led me to build Distill - deterministic context deduplication for LLMs. No LLM calls, no embeddings, no probabilistic heuristics. Pure algorithms that clean your context in ~12ms. I wanted at least one part of the AI pipeline to be something I could reason about, debug, and trust. If the model’s output is going to be nondeterministic, the least I can do is make sure the input is clean and predictable.
The engineers I’ve talked to who handle this best are the ones who’ve made peace with it. They treat AI output like a first draft from a smart but unreliable intern. They expect to rewrite 30% of it. They budget time for that rewriting. They don’t get frustrated when the output is wrong because they never expected it to be right. They expected it to be useful. There’s a difference.
Take a breath and try to keep up with just the last few months. Claude Code ships sub-agents, then skills, then an Agent SDK, then Claude Cowork. OpenAI launches Codex CLI, then GPT-5.3-Codex - a model that literally helped code itself. New coding agents announce background mode with hundreds of concurrent autonomous sessions. Google drops Gemini CLI. GitHub adds an MCP Registry. Acquisitions happen weekly. Amazon Q Developer gets agentic upgrades. CrewAI, AutoGen, LangGraph, MetaGPT - pick your agent framework, there’s a new one every week. Google announces A2A (Agent-to-Agent protocol) to compete with Anthropic’s MCP. OpenAI ships its own Swarm framework. Kimi K2.5 drops with agent swarm architecture orchestrating 100 parallel agents. “Vibe coding” becomes a thing. OpenClaw launches a skills marketplace and within one week, researchers find 400+ malicious agent skills uploaded to ClawHub. And somewhere in the middle of all this, someone on LinkedIn posts “if you’re not using AI agents with sub-agent orchestration in 2026, you’re already obsolete.”
That’s not a year. That’s a few months. And I’m leaving stuff out.
I fell into this trap hard. I was spending weekends evaluating new tools. Reading every changelog. Watching every demo. Trying to stay at the frontier because I was terrified of falling behind.
Here’s what that actually looked like: I’d spend Saturday afternoon setting up a new AI coding tool. By Sunday I’d have a basic workflow. By the following Wednesday, someone would post about a different tool that was “way better.” I’d feel a pang of anxiety. By the next weekend, I’d be setting up the new thing. The old thing would sit unused. One coding assistant to the next to the next and back to the first one. Each migration cost me a weekend and gave me maybe a 5% improvement that I couldn’t even measure properly.
Multiply this by every category - coding assistants, chat interfaces, agent frameworks, multi-agent orchestration platforms, MCP servers, context management tools, prompt libraries, swarm architectures, skills marketplaces - and you get a person who is perpetually learning new tools and never getting deep with any of them. The Hacker News front page alone is enough to give you whiplash. One day it’s “Show HN: Autonomous Research Swarm” and the next it’s “Ask HN: How will AI swarms coordinate?” Nobody knows. Everyone’s building anyway.
The worst part is the knowledge decay. I spent two weeks building a sophisticated prompt engineering workflow in early 2025. Carefully crafted system prompts, few-shot examples, chain-of-thought templates. It worked well. Three months later, the model updated, the prompting best practices shifted, and half my templates produced worse results than a simple one-liner. Those two weeks were gone. Not invested. Spent. The same thing happened with my MCP server setup - I built five custom servers (Dev.to publisher, Apple Notes integration, Python and TypeScript sandboxes, more), then the protocol evolved, then the MCP Registry launched on GitHub and suddenly there were thousands of pre-built ones. Some of my custom work became redundant overnight.
The agent framework churn is even worse. I watched teams go from LangChain to CrewAI to AutoGen to custom orchestration in the span of a year. Each migration meant rewriting integrations, relearning APIs, rebuilding workflows. The people who waited and did nothing often ended up in a better position than the people who adopted early and had to migrate twice.
I’ve since adopted a different approach. Instead of chasing every new tool, I go deep on the infrastructure layer underneath them. Tools come and go. The problems they solve don’t. Context efficiency, agent authorization, audit trails, runtime security - these are durable problems regardless of which framework is trending this month. That’s why I built agentic-authz on OpenFGA instead of tying it to any specific agent framework. That’s why Distill works at the context level, not the prompt level. Build on the layer that doesn’t churn.
I still track the landscape closely - you have to when you’re building infrastructure for it. But I track it to understand where the ecosystem is going, not to adopt every new thing. There’s a difference between being informed and being reactive.
This one is insidious. You’re trying to get AI to generate something specific. The first output is 70% right. So you refine your prompt. The second output is 75% right but broke something the first one had correct. Third attempt: 80% right but now the structure is different. Fourth attempt: you’ve been at this for 45 minutes and you could have written the thing from scratch in 20.
I call this the prompt spiral. It’s the AI equivalent of yak shaving. You started with a clear goal. Thirty minutes later you’re debugging your prompt instead of debugging your code. You’re optimizing your instructions to a language model instead of solving the actual problem.
The prompt spiral is especially dangerous because it feels productive. You’re iterating. You’re getting closer. Each attempt is slightly better. But the marginal returns are diminishing fast, and you’ve lost sight of the fact that the goal was never “get the AI to produce perfect output.” The goal was to ship the feature.
I now have a hard rule: three attempts. If the AI doesn’t get me to 70% usable in three prompts, I write it myself. No exceptions. This single rule has saved me more time than any prompting technique I’ve ever learned.
Engineers tend toward perfectionism. We like clean code. We like tests that pass. We like systems that behave predictably. This is a feature, not a bug - it’s what makes us good at building reliable software.
AI output is never perfect. It’s always “pretty good.” 70-80% there. The variable names are slightly off. The error handling is incomplete. The edge cases are ignored. The abstraction is wrong for your codebase. It works, but it’s not right.
For a perfectionist, this is torture. Because “almost right” is worse than “completely wrong.” Completely wrong, you throw away and start over. Almost right, you spend an hour tweaking. And tweaking AI output is uniquely frustrating because you’re fixing someone else’s design decisions - decisions that were made by a system that doesn’t share your taste, your context, or your standards.
I had to learn to let go. Not of quality - I still care about quality. But of the expectation that AI would produce quality. I now treat every AI output as a rough draft. A starting point. Raw material. I mentally label it “draft” the moment it appears, and that framing change alone reduced my frustration by half.
The engineers who struggle most with AI are often the best engineers. The ones with the highest standards. The ones who notice every imperfection. AI rewards a different skill: the ability to extract value from imperfect output quickly, without getting emotionally invested in making it perfect.
This is the one that scares me most.
I noticed it during a design review meeting. Someone asked me to reason through a concurrency problem on the whiteboard. No laptop. No AI. Just me and a marker. And I struggled. Not because I didn’t know the concepts - I did. But because I hadn’t exercised that muscle in months. I’d been outsourcing my first-draft thinking to AI for so long that my ability to think from scratch had degraded.
It’s like GPS and navigation. Before GPS, you built mental maps. You knew your city. You could reason about routes. After years of GPS, you can’t navigate without it. The skill atrophied because you stopped using it.
The same thing is happening with AI and engineering thinking. When you always ask AI first, you stop building the neural pathways that come from struggling with a problem yourself. The struggle is where learning happens. The confusion is where understanding forms. Skip that, and you get faster output but shallower understanding.
I now deliberately spend the first hour of my day without AI. I think on paper. I sketch architectures by hand. I reason through problems the slow way. It feels inefficient. It is inefficient. But it keeps my thinking sharp, and that sharpness pays dividends for the rest of the day when I do use AI - because I can evaluate its output better when my own reasoning is warmed up.
Social media is full of people who seem to have AI figured out. They post their workflows. Their productivity numbers. Their “I built this entire app in 2 hours with AI” threads. And you look at your own experience - the failed prompts, the wasted time, the code you had to rewrite - and you think: what’s wrong with me?
Nothing is wrong with you. Those threads are highlight reels. Nobody posts “I spent 3 hours trying to get Claude to understand my database schema and eventually gave up and wrote the migration by hand.” Nobody posts “AI-generated code caused a production incident because it silently swallowed an error.” Nobody posts “I’m tired.”
The comparison trap is amplified by the fact that AI skill is hard to measure. With traditional engineering, you can look at someone’s code and roughly gauge their ability. With AI, the output depends on the model, the prompt, the context, the temperature, the phase of the moon. Someone’s impressive demo might not reproduce on your machine with your codebase.
I became much more selective about AI content on social media. I still follow the space closely - I have to, it’s my job. But I shifted from consuming everyone’s hot takes to focusing on people who are actually building and shipping, not just demoing. The ratio of signal to anxiety matters. If a feed is making you feel behind instead of informed, it’s not serving you.
I’ll be specific about what changed my relationship with AI from adversarial to sustainable.
Time-boxing AI sessions. I don’t use AI in an open-ended way anymore. I set a timer. 30 minutes for this task with AI. When the timer goes off, I ship what I have or switch to writing it myself. This prevents the prompt spiral and the perfectionism trap simultaneously.
Separating AI time from thinking time. Morning is for thinking. Afternoon is for AI-assisted execution. This isn’t rigid - sometimes I break the rule. But having a default structure means my brain gets both exercise and assistance in the right proportions.
Accepting 70% from AI. I stopped trying to get perfect output. 70% usable is the bar. I’ll fix the rest myself. This acceptance was the single biggest reducer of AI-related frustration in my workflow.
Being strategic about the hype cycle. I track the AI landscape because I build infrastructure for it. But I stopped adopting every new tool the week it launches. I use one primary coding assistant and know it deeply. I evaluate new tools when they’ve proven themselves over months, not days. Staying informed and staying reactive are different things.
Logging where AI helps and where it doesn’t. I kept a simple log for two weeks: task, used AI (yes/no), time spent, satisfaction with result. The data was revealing. AI saved me significant time on boilerplate, documentation, and test generation. It cost me time on architecture decisions, complex debugging, and anything requiring deep context about my codebase. Now I know when to reach for it and when not to.
Not reviewing everything AI produces. This was hard to accept. But if you’re using AI to generate large amounts of code, you physically cannot review every line with the same rigor. I focus my review energy on the parts that matter most - security boundaries, data handling, error paths - and rely on automated tests and static analysis for the rest. Some roughness in non-critical code is acceptable.
The tech industry has a burnout problem that predates AI. AI is making it worse, not better. Not because AI is bad, but because AI removes the natural speed limits that used to protect us.
Before AI, there was a ceiling on how much you could produce in a day. That ceiling was set by typing speed, thinking speed, the time it takes to look things up. It was frustrating sometimes, but it was also a governor. You couldn’t work yourself to death because the work itself imposed limits.
AI removed the governor. Now the only limit is your cognitive endurance. And most people don’t know their cognitive limits until they’ve blown past them.
I burned out in late 2025. Not dramatically - I didn’t quit or have a breakdown. I just stopped caring. Code reviews became rubber stamps. Design decisions became “whatever AI suggests.” I was going through the motions, producing more than ever, feeling less than ever. It took me a month to realize what had happened and another month to recover.
The recovery wasn’t about using less AI. It was about using AI differently. With boundaries. With intention. With the understanding that I am not a machine and I don’t need to keep pace with one. Working at Ona helped me see this clearly - when you’re building AI agent infrastructure for enterprise customers, you see the human cost of unsustainable AI workflows at scale. The problems aren’t just personal. They’re systemic. And they need to be solved at the tooling level, not just the individual level.
Ironically, the burnout period is when some of my best work happened. When I stopped trying to use every AI tool and started thinking about what was actually broken, I saw the problems clearly for the first time. Context windows filling up with garbage - that became Distill. Agents with all-or-nothing API key access - that became agentic-authz. The inability to audit what an agent actually did - that’s becoming AgentTrace. The fatigue forced me to stop consuming and start building. Not building more features faster, but building the right things deliberately.
Here’s what I think the real skill of the AI era is. It’s not prompt engineering. It’s not knowing which model to use. It’s not having the perfect workflow.
It’s knowing when to stop.
Knowing when the AI output is good enough. Knowing when to write it yourself. Knowing when to close the laptop. Knowing when the marginal improvement isn’t worth the cognitive cost. Knowing that your brain is a finite resource and that protecting it is not laziness - it’s engineering.
We optimize our systems for sustainability. We add circuit breakers. We implement backpressure. We design for graceful degradation. We should do the same for ourselves.
AI is the most powerful tool I’ve ever used. It’s also the most draining. Both things are true. The engineers who thrive in this era won’t be the ones who use AI the most. They’ll be the ones who use it the most wisely.
If you’re tired, it’s not because you’re doing it wrong. It’s because this is genuinely hard. The tool is new, the patterns are still forming, and the industry is pretending that more output equals more value. It doesn’t. Sustainable output does.
I’m still building in this space every day. Agent authorization, context engineering, audit trails, runtime security - the infrastructure that makes AI agents actually work in production. I’m more committed to AI than ever. But I’m committed on my terms, at my pace, building things that matter instead of chasing things that trend.
Take care of your brain. It’s the only one you’ve got, and no AI can replace it.
If this resonated, I’d love to hear your experience. What does AI fatigue look like for you? Find me on X or LinkedIn, or join the discussion on Hacker News.
I write about AI agent infrastructure, security, context engineering, and the human side of building with AI. You can find all my writing on my writing page.
...
Read the original on siddhantkhare.com »
We’re excited to announce that DoNotNotify has been open sourced. The full source code for the app is now publicly available for anyone to view, study, and contribute to.
You can find the source code on GitHub:
...
Read the original on donotnotify.com »
I felt the familiar feeling of depression and lethargy creep in while my eyes darted from watching claude-code work and my phone. “What’s the point of it all?” I thought, LLMs can generate decent-ish and correct-ish looking code while I have more time to do what? doomscroll? This was the third time I gave claude-code a try. I felt the same feelings every single time and ended up deleting claude-code after 2-3 weeks, and whaddyouknow? Every. Single. Time. I rediscovered the joy of coding.
Yes, coding is not software engineering, but for me, it is a fun and essential part of it. In order to be effective at software engineering, you must be familiar with the problem space, and this requires thinking and wrestling with the problem. You can’t truly know the pain of using an API by just reading its documentation or implementation. You have to use it to experience it. The act of writing code, despite being slower, was a way for me to wrestle with the problem space, a way for me to find out that my initial ideas didn’t work, a way for thinking. Vibe coding interfered with that.
If you’re thinking without writing, you only think you’re thinking.
The other major part of the job is to ensure correctness. For me, it is much harder to verify the correctness of code I didn’t write compared to code I wrote. The process of writing code helps internalize the context and is easier for my brain to think deeply about it. If I outsource this to an LLM, I skip over the process of internalizing the problem domain and I can’t be certain that the generated code is correct.
By design, vibe coding has an addictive nature to it, you write some instructions, and code that looks correct is generated. Bam! Dopamine hit! If the code isn’t correct, then it’s just one prompt away from being correct, right? right?
Vibe coding also has the profound effect of turning my brain off and passively accepting changes. When it is time to use my brain, the inertia is much harder to overcome and it is easy to choose the lazy way out. At my lowest point, I even asked it to do a find-and-replace in a file. Something that takes a few seconds, now took minutes and a network call.
Even if I generate a 1,000 line PR in 30 minutes I still need to understand and review it. Since I am responsible for the code I ship, this makes me the bottleneck.
The common view of vibe coding is that it is neither good nor bad, it is a tool. But tools shape your workflow and your thought process, and if a tool prevents you from thinking deeply, I don’t think it is a good tool. If you are a knowledge worker, your core competency is your ability to think, and if a tool interferes with that, be afraid, be very afraid.
Now, I would be lying if I said I didn’t use LLMs to generate code. I still use Claude, but I do so in a more controlled manner. I copy-paste files that I think are necessary to provide the context, and then I copy-paste code and ask it to make changes to it or write tests for it. This friction has several benefits. I can’t make changes that span multiple files, this means the generated diff isn’t too large, and if I have to manually change other files I know how the code fits in. Manually giving claude the context forces me to be familiar with the codebase myself, rather than tell it to just “cook”. It turns code generation from a passive action to a deliberate thoughtful action. It also keeps my brain engaged and active, which means I can still enter the flow state. I have found this to be the best of both worlds and a way to preserve my happiness at work.
Ultimately, life is too short to not optimize for happiness. Maybe (a big maybe) generating entire features would make me more productive, but if it causes existential dread and makes me depressed, I don’t see it being productive in the long run. Maybe you relate to some of the feelings. Maybe you don’t. But don’t be afraid to choose differently.
...
Read the original on abhinavomprakash.com »
A local device focused AI assistant built in Rust — persistent memory, autonomous tasks, ~27MB binary. Inspired by and compatible with OpenClaw.
* Local device focused — runs entirely on your machine, your memory data stays yours
* Autonomous heartbeat — delegate tasks and let it work in the background
# Full install (includes desktop GUI)
cargo install localgpt
# Headless (no desktop GUI — for servers, Docker, CI)
cargo install localgpt –no-default-features
# Initialize configuration
localgpt config init
# Start interactive chat
localgpt chat
# Ask a single question
localgpt ask “What is the meaning of life?”
# Run as a daemon with heartbeat, HTTP API and web ui
localgpt daemon start
LocalGPT uses plain markdown files as its memory:
Files are indexed with SQLite FTS5 for fast keyword search, and sqlite-vec for semantic search with local embeddings
[agent]
default_model = “claude-cli/opus”
[providers.anthropic]
api_key = “${ANTHROPIC_API_KEY}”
[heartbeat]
enabled = true
interval = “30m”
active_hours = { start = “09:00”, end = “22:00″ }
[memory]
workspace = “~/.localgpt/workspace”
# Chat
localgpt chat # Interactive chat
localgpt chat –session
When the daemon is running:
Why I Built LocalGPT in 4 Nights — the full story with commit-by-commit breakdown.
...
Read the original on github.com »
What if this is as good as software is ever going to be? What if AI stops getting better and what if people stop caring?
Imagine if this is as good as AI gets. If this is where it stops, you’d still have models that can almost code a web browser, almost code a compiler—and can even present a pretty cool demo if allowed to take a few shortcuts. You’d still get models that can kinda-sorta simulate worlds and write kinda-sorta engaging stories. You’d still get self-driving cars that almost work, except when they don’t. You get AI that can make you like 90% of a thing!
90% is a lot. Will you care about the last 10%?
I’m terrified of the good enough to ship—and I’m terrified of nobody else caring. I’m less afraid of AI agents writing apps that they will never experience than I am of the AI herders who won’t care enough to actually learn what they ship. And I sure as hell am afraid of the people who will experience the slop and will be fine with it.
As a woodworking enthusiast I am slowly making my peace with standing in the middle of an IKEA. But at the rate things are going in this dropshipping hell, IKEA would be the dream. Software temufication stings much more than software commoditization.
I think Claude and friends can help with crafting good software and with learning new technologies and programming languages—though I sure as hell move slower when I stop to learn and understand than the guy playing Dwarf Fortress with 17 agents. But at the same time AI models seem to constantly nudge towards that same median Next-React-Tailwind, good enough app. These things just don’t handle going off the beaten path well.
Spend all the tokens you want, trying to make something unique like Paper by FiftyThree with AI tools will just end up looking normal and uninspired.
Mind you, it’s not like slop is anything new. A lot of human decisions had to happen before your backside ended up in an extremely uncomfortable chair, your search results got polluted by poorly-written SEO-optimized articles, and your brain had to deal with a ticket booking website with a user interface so poorly designed that it made you cry. So it’s a people problem. Incentives just don’t seem to align to make good software. Move fast and break things, etc, etc. You’ll make a little artisan app, and if it’s any good, Google will come along with a free clone, kill you, then kill its clone—and the world will be left with net zero new good software. And now, with AI agents, it gets even worse as agent herders can do the same thing much faster.
Developers aside, there’s also the users. AI models can’t be imaginative, and the developers can’t afford to, but surely with AI tools, the gap between users and developers will be bridged, ChatGPT will become the new HyperCard and people will turn their ideas into reality with just a few sentences? There’s so many people out there who are coding without knowing it, from Carol in Accounting making insane Excel spreadsheets to all the kids on TikTok automating their phones with Apple Shortcuts and hacking up cool Notion notebooks.
But what if those people are an aberration? What if this state of tech learned helplessness cannot be fixed? What if people really do just want a glorified little TV in their pocket? What if most people truly just don’t care about tech problems, about privacy, about Liquid Glass, about Microsoft’s upsells, about constantly dealing with apps and features which just don’t work? What if there will be nobody left to carry the torch? What if the future of computing belongs not to artisan developers or Carol from Accounting, but to whoever can churn out the most software out the fastest? What if good enough really is good enough for most people?
I’m terrified that our craft will die, and nobody will even care to mourn it.
...
Read the original on ezhik.jp »
The modern Olympics sell themselves on a simple premise: the whole world, watching the same moment, at the same time. On Friday night in Milan, that illusion fractured in real time.
When Team USA entered the San Siro during the parade of nations, the speed skater Erin Jackson led the delegation into a wall of cheers. Moments later, when cameras cut to US vice-president JD Vance and second lady Usha Vance, large sections of the crowd responded with boos. Not subtle ones, but audible and sustained ones. Canadian viewers heard them. Journalists seated in the press tribunes in the upper deck, myself included, clearly heard them. But as I quickly realized from a groupchat with friends back home, American viewers watching NBC did not.
On its own, the situation might once have passed unnoticed. But the defining feature of the modern sports media landscape is that no single broadcaster controls the moment any more. CBC carried it. The BBC liveblogged it. Fans clipped it. Within minutes, multiple versions of the same happening were circulating online — some with boos, some without — turning what might once have been a routine production call into a case study in information asymmetry.
For its part, NBC has denied editing the crowd audio, although it is difficult to resolve why the boos so audible in the stadium and on other broadcasts were absent for US viewers. But in a broader sense, it is becoming harder, not easier, to curate reality when the rest of the world is holding up its own camera angles. And that raises an uncomfortable question as the United States moves toward hosting two of the largest sporting events on the planet: the 2026 men’s World Cup and the 2028 Los Angeles Olympics.
If a US administration figure is booed at the Olympics in Los Angeles, or a World Cup match in New Jersey or Dallas, will American domestic broadcasts simply mute or avoid mentioning the crowd audio? If so, what happens when the world feed, or a foreign broadcaster, shows something else entirely? What happens when 40,000 phones in the stadium upload their own version in real time?
The risk is not just that viewers will see through it. It is that attempts to manage the narrative will make American broadcasters look less credible, not more. Because the audience now assumes there is always another angle. Every time a broadcaster makes that trade — credibility for insulation — it is a trade audiences eventually notice.
There is also a deeper structural pressure behind decisions like this. The Trump era has been defined in part by sustained hostility toward media institutions. Broadcasters do not operate in a vacuum; they operate inside regulatory environments, political climates and corporate risk calculations. When presidents and their allies openly threaten or target networks, it is naive to pretend that has no downstream effect on editorial choices — especially in high-stakes live broadcasts tied to billion-dollar rights deals.
But there is a difference between contextual pressure and visible reality distortion. When global audiences can compare feeds in real time, the latter begins to resemble something else entirely: not editorial judgment, but narrative management. Which is why comparisons to Soviet-style state-controlled broadcasting models — once breathless rhetorical exaggerations — are starting to feel less hyperbolic.
The irony is that the Olympics themselves are built around the idea that sport can exist alongside political tension without pretending it does not exist. The International Olympic Committee’s own language — athletes should not be punished for governments’ actions — implicitly acknowledges that governments are part of the Olympic theater whether organizers like it or not.
Friday night illustrated that perfectly. American athletes were cheered, their enormous contingent given one of the most full-throated receptions of the night. The political emissaries were not universally welcomed. Both things can be true at once. Crowd dissent is not a failure of the Olympic ideal. In open societies, it is part of how public sentiment is expressed. Attempting to erase one side of that equation risks flattening reality into something audiences no longer trust. And if Milan was a warning shot, Los Angeles is the main event.
Since Donald Trump’s first term, American political coverage around sport has fixated on the micro-moments: Was the president booed or cheered? Did the broadcast show it? Did he attend or skip events likely to produce hostile crowds? The discourse has often felt like a Rorschach test, filtered through partisan interpretation and selective clips.
The LA Olympics will be something else entirely. There is no hiding from an opening ceremony for Trump. No ducking a stadium when the Olympic Charter requires the host country’s head of state to officially declare the Games open. No controlling how 200 international broadcasters carry the moment.
If Trump is still in the White House on 14 July 2028, one month after his 82nd birthday and in the thick of another heated US presidential campaign, he will stand in front of a global television audience as a key part of the opening ceremony. He will do so in California, in a political environment far less friendly than many domestic sporting venues he has appeared in over the past decade. And he will do it in a city synonymous with the political opposition, potentially in the back yard of the Democratic presidential candidate.
There will be some cheers. There will almost certainly be boos. There will be everything in between. And there will be no way to make them disappear. The real risk for American broadcasters is not that dissent will be visible. It is that audiences will start assuming anything they do not show is being hidden. In an era when trust in institutions is already fragile, that is a dangerous place to operate from.
The Olympics have always been political, whether through boycotts, protests, symbolic gestures or crowd reactions. What has changed is not the politics. It is the impossibility of containing the optics.
Milan may ultimately be remembered as a small moment — a few seconds of crowd noise during a long ceremony. But it also felt like a preview of the next phase of global sport broadcasting: one where narrative control is shared, contested and instantly verifiable. The world is watching. And this time, it is also recording.
...
Read the original on www.theguardian.com »
I’m generally pretty pro-AI with one major exception: agentic coding. My consistent impression is that agentic coding does not actually improve productivity and deteriorates the user’s comfort and familiarity with the codebase. I formed that impression from:
Every time I use agentic coding tools I’m consistently unimpressed with the quality of the results.
I allow interview candidates to use agentic coding tools and candidates who do so consistently performed worse than other candidates, failing to complete the challenge or producing incorrect results1. This was a huge surprise to me at first because I expected agentic coding to confer an unfair advantage but … nope!
Studies like the Becker study and Shen study show that users of agentic coding perform no better and sometimes worse when you measure productivity in terms of fixed outcomes rather than code velocity/volume.
I don’t believe agentic coding is a lost cause, but I do believe agentic coding in its present incarnation is doing more harm than good to software development. I also believe it is still worthwhile to push on the inadequacies of agentic coding so that it empowers developers and improves code quality.
However, in this post I’m taking a different tack: I want to present other ways to leverage AI for software development. I believe that agentic coding has so captured the cultural imagination that people are sleeping on other good and underexplored solutions to AI-assisted software development.
I like to design tools and interfaces from first principles rather than reacting to industry trends/hype and I’ve accrued quite a few general design principles from over a decade of working in DevProd and also an even longer history of open source projects and contributions.
One of those design principles is my personal “master cue”, which is:
A good tool or interface should keep the user in a flow state as long as possible
This principle isn’t even specific to AI-assisted software development, and yet still highlights why agentic coding sometimes misses the mark. Both studies and developer testimonials show that agentic coding breaks flow and keeps developers in an idle/interruptible holding pattern more than ordinary coding.
For example, the Becker study took screen recordings and saw that idle time approximately doubled:
I believe we can improve AI-assisted coding tools (agentic or not) if we set our north star to “preserve flow state”.
Calm technology is a design discipline that promotes flow state in tools that we build. The design principles most relevant to coding are:
tools should minimize demands on our attention
Interruptions and intrusions on our attention break us out of flow state.
tools should be built to be “pass-through”
A tool is not meant to be the object of our attention; rather the tool should reveal the true object of our attention (the thing the tool acts upon), rather than obscuring it. The more we use the tool the more the tool fades into the background of our awareness while still supporting our work.
tools should create and enhance calm (thus the name: calm technology)
Engineers already use “calm” tools and interfaces as part of our work and here are a couple of examples you’re probably already familiar with:
IDEs (like VSCode) can support inlay hints that sprinkle the code with useful annotations for the reader, such as inferred type annotations:
These types of inlay hints embody calm design principles because:
they minimize demands on our attention
They exist on the periphery of our attention, available for us if we’re interested but unobtrusive if we’re not interested.
they are built to be “pass-through”
They don’t replace or substitute the code that we are editing. They enhance the code editing experience but the user is still in direct contact with the edited code. The more we use type hints the more they fade into the background of our awareness and the more the code remains the focus of our attention.
They promote a sense of calm by informing our understanding of the code passively. As one of the Calm Technology principles puts it: “Technology can communicate, but doesn’t need to speak”.
Tools like VSCode or GitHub’s pull request viewer let you preview at a glance changes to the file tree, like this:
You might think to yourself “this is a very uninteresting thing to use as an example” but that’s exactly the point. The best tools (designed with the principles of calm technology) are pervasive and boring things that we take for granted (like light switches) and that have faded so strongly into the background of our attention that we forget they even exist as a part of our daily workflow (also like light switches).
They’re there if we need the information, but easy to ignore (or even forget they exist) if we don’t use them.
are built to be “pass-through”
When we interact with the file tree viewer we are interacting directly with the filesystem and the interaction between the representation (the viewer) and the reality (the filesystem) feels direct, snappy, and precise. The more we use the viewer the more the representation becomes indistinguishable from the reality in our minds.
We do not need to constantly interact with the file tree to gather up-to-date information about our project structure. It passively updates in the background as we make changes to the project and those updates are unobtrusive and not attention-grabbing.
We can think about the limitations of chat-based agentic coding tools through this same lens:
they place high demands on our attention
The user has to either sit and wait for the agent to report back or do something else and run the LLM in a semi-autonomous manner. However, even semi-autonomous sessions prevent the user from entering flow state because they have to remain interruptible.
they are not built to be “pass-through”
Chat agents are a highly mediated interface to the code which is indirect (we interact more with the agent than the code), slow (we spend a lot of time waiting), and imprecise (English is a dull interface).
The user needs to constantly stimulate the chat to gather new information or update their understanding of the code (the chat agent doesn’t inform the user’s understanding passively or quietly). Chat agents are also fine-tuned to maximize engagement.
One of the earliest examples of an AI coding assistant that begins to model calm design principles is the OG AI-assistant: GitHub Copilot’s support for inline suggestions, with some caveats I’ll go into.
This does one thing really well:
it’s built to be “pass-through”
The user is still interacting directly with the code and the suggestions are reasonably snappy. The user can also ignore or type through the suggestion.
However, by default these inline suggestions violate other calm technology principles:
By default Copilot presents the suggestions quite frequently and the user has to pause what they’re doing to examine the output of the suggestion. After enough times the user begins to condition themselves into regularly pausing and waiting for a suggestion which breaks them out of a flow state. Now instead of being proactive the user’s been conditioned by the tool to be reactive.
GitHub Copilot’s inline suggestion interface is visually busy and intrusive. Even if the user ignores every suggestion the effect is still disruptive: suggestions appear on the user’s screen in the center of their visual focus and the user has to decide on the spot whether to accept or ignore them before proceeding further. The user also can’t easily passively absorb information presented in this way: understanding each suggestion requires the user’s focused attention.
… buuuuut these issues are partially fixable by disabling the automatic suggestions and requiring them to be explicitly triggered by Alt + \. However, unfortunately that also disables the next feature, which I like even more:
Next edit suggestions (also from GitHub Copilot)
Next edit suggestions are a related GitHub Copilot feature that display related follow-up edits throughout the file/project and let the user cycle between them and possibly accept each suggested change. They behave like a “super-charged find and replace”:
These suggestions do an amazing job of keeping the user in a flow state:
they minimize demand on the user’s attention
The cognitive load on the user is smaller than inline suggestions because the suggestions are more likely to be bite-sized (and therefore easier for a human to review and accept).
Just like inline suggestions, next edit suggestions still keep the user in close contact with the code they are modifying.
Suggestions are presented in an unobtrusive way: they aren’t dumped in the dead center of the user’s attention and they don’t demand immediate review. They exist on the periphery of the user’s attention as code suggestions that the user can ignore or focus on at their leisure.
I believe there is a lot of untapped potential in AI-assisted coding tools and in this section I’ll sketch a few small examples of how we can embody calm technology design principles in building the next generation of coding tools.
You could browse a project by a tree of semantic facets. For example, if you were editing the Haskell implementation of Dhall the tree viewer might look like this prototype I hacked up2:
The goal here is to not only provide a quick way to explore the project by intent, but to also improve the user’s understanding of the project the more they use the feature. “String interpolation regression” is so much more informative than dhall/tests/format/issue2078A.dhall3.
Also, the above video is based on a real tool and not just a mock. You can find the code I used to generate that tree of semantics facets here and I’ll write up another post soon walking through how that code works.
You could take an editor session, a diff, or a pull request and automatically split it into a series of more focused commits that are easier for people to review. This is one of the cases where the AI can reduce human review labor (most agentic coding tools create more human review labor).
There is some prior art here but this is still a nascent area of development.
You could add two new tools to the user’s toolbar or context menu: “Focus on…” and “Edit as…”.
“Focus on…” would allow the user to specify what they’re interested in changing and present only files and lines of code related to their specified interest. For example, if they want to focus on “command line options” then only related files and lines of code would be shown in the editor and other lines of code would be hidden/collapsed/folded. This would basically be like “Zen mode” but for editing a feature domain of interest.
“Edit as…” would allow the user to edit the file or selected code as if it were a different programming language or file format. For example, someone who was new to Haskell could edit a Haskell file “as Python” and then after finishing their edits the AI attempts to back-propagate their changes to Haskell. Or someone modifying a command-line parser could edit the file “as YAML” and be presented with a simplified YAML representation of the command line options which they could modify to add new options.
This is obviously not a comprehensive list of ideas, but I wrote this to encourage people to think of more innovative ways to incorporate AI into people’s workflows besides just building yet another chatbot. I strongly believe that chat is the least interesting interface to LLMs and AI-assisted software development is no exception to this.
Copyright © 2026 Gabriella Gonzalez. This work is licensed under CC BY-SA 4.0
...
Read the original on haskellforall.com »
If you use an Apple silicon Mac I’m sure you have been impressed by its performance. Whether you’re working with images, audio, video or building software, we’ve enjoyed a new turn of speed since the M1 on day 1. While most attribute this to their Performance cores, as it goes with the name, much is in truth the result of the unsung Efficiency cores, and how they keep background tasks where they should be.
To see what I mean, start your Apple silicon Mac up from the cold, and open Activity Monitor in its CPU view, with its CPU History window open as well. For the first five to ten minutes you’ll see its E cores are a wall of red and green with Spotlight’s indexing services, CGPDFService, mediaanalysisd, BackgroundShortcutRunner, Siri components, its initial Time Machine backup, and often an XProtect Remediator scan. Meanwhile its P cores are largely idle, and if you were to dive straight into using your working apps, there’s plenty of capacity for them to run unaffected by all that background mayhem.
It’s this stage that scares those who are still accustomed to using Intel Macs. Seeing processes using more than 100% CPU is terrifying, because they know that Intel cores can struggle under so much load, affecting user apps. But on an Apple silicon Mac, who notices or cares that there’s over a dozen mdworker processes each taking a good 50% CPU simultaneously? After all, this is what the Apple silicon architecture is designed for. Admittedly the impression isn’t helped by a dreadful piece of psychology, as those E cores at 100% are probably running at a frequency a quarter of those of P cores shown at the same 100%, making visual comparison completely false.*
This is nothing new. Apple brought it to the iPhone 7 in 2016, in its first SoC with separate P and E cores. That’s an implementation of Arm’s big. LITTLE announced in 2011, and development work at Cray and elsewhere in the previous decade. What makes the difference in Apple silicon Macs is how threads are allocated to the two different CPU core types on the basis of a metric known as Quality of Service, or QoS.
As with so much in today’s Macs, QoS has been around since OS X 10.10 Yosemite, six years before it became so central in performance. When all CPU cores are the same, it has limited usefulness over more traditional controls like Posix’s nice scheduling priority. All those background tasks still have to be completed, and giving them a lower priority only prolongs the time they take on the CPU cores, and the period in which the user’s apps are competing with them for CPU cycles.
With the experience gained from its iPhones and other devices, Apple’s engineers had a better solution for future Macs. In addition to providing priority-based queues, QoS makes a fundamental distinction between those threads run in the foreground, and those of the background. While foreground threads will be run on P cores when they’re available, they can also be scheduled on E cores when necessary. But background threads aren’t normally allowed to run on P cores, even if they’re delayed by the load on the E cores they’re restricted to. We know this from our inability to promote existing background threads to run on P cores using St. Clair Software’s App Tamer and the command tool taskpolicy.
This is why, even if you sit and watch all those background processes loading the E cores immediately after starting up, leaving the P cores mostly idle, macOS won’t try running them on its P cores. If it did, even if you wanted it to, the distinction between foreground and background, P and E cores would start to fall apart, our apps would suffer as a consequence, and battery endurance would decline. Gone are the days of crashing mdworker processes bringing our Macs to their knees with a spinning beachball every few seconds.
If seeing all those processes using high % CPU can look scary, the inevitable consequence in terms of software architecture might seem terrifying. Rather than building monolithic apps, many of their tasks are now broken out into discrete processes run in the background on demand, on the E cores when appropriate. The fact that an idle Mac has over 2,000 threads running in over 600 processes is good news, and the more of those that are run on the E cores, the faster our apps will be. The first and last M-series chips to have only two E cores were the M1 Pro and Max, since when every one has had at least four E cores, and some as many as six or eight.
Because Efficiency cores get the background threads off the cores we need for performance.
* For the record, I have measured those frequencies using powermetrics. For an M4 Pro, for example, high QoS threads running on the P cores benefit from frequencies close to the P core max of 4,512 MHz. Low QoS threads running on the E cores are run at frequencies close to idle, typically around 1,050 MHz. However, when the E cores run high QoS threads that have overflowed from the P cores, the E cores are normally run at around their maximum of 2,592 MHz. By my arithmetic, 1,050 divided by 4,512 is 0.233, which is slightly less than a quarter. Other M-series chips are similar.
...
Read the original on eclecticlight.co »
Check out the code, download the ROMsMaking it work on the Game BoyThe Game Boy has no multiply instructionAll scalars and lookups are 8-bit fractionsHow fast is it?An overall failed attempt at using AI
Check out the code, download the ROMsMaking it work on the Game BoyThe Game Boy has no multiply instructionAll scalars and lookups are 8-bit fractionsHow fast is it?An overall failed attempt at using AI
I made a Game Boy Color game that renders images in real time. The player controls an orbiting light and spins an object.
Before really diving into this project, I experimented with the look in Blender to see if it would even look good. IMO it did, so I went ahead with it!
I experimented with a “pseudo-dither” on the Blender monkey by adding a small random vector to each normal.
It doesn’t really matter what software I used to produce the normal maps. Blender was the path of least resistance for me, so I chose that.
For the teapot, I simply put in a teapot, rotated a camera around it, and exported the normal AOV as a PNG sequence. Pretty straight-forward.
For the spinning Game Boy Color, I wanted to ensure that certain colors were solid, so I used cryptomattes in the compositor to identify specific geometry and output hard-coded values in the output.
The geometry in the screen was done by rendering a separate scene, then compositing it in the final render using a cryptomatte for the screen.
The above animations are normal map frames that are used to solve the value of each pixel
Normal maps are a core concept of this project. They’re already used everywhere in 3D graphics.
And indeed, normal map images are secretly a vector field. The reason normal maps tend to have a blue-ish baseline color, is because everyone likes to associate XYZ with RGB, and +Z is the forward vector by convention.
In a typical 3D workflow, a normal map is used to encode the normal vector at any given point on a textured mesh.
The simplest way to shade a 3D object is using the dot product:
where is the normal vector, and is the light position when it points towards the origin (or equivalently: the negative light direction).
Expanded out component-wise, this is:
When the light vector is constant for all pixels, it models what most 3D graphics software calls a “distant light”, or a “sun light”.
To speed up computation on the Game Boy, I use an alternate version of the dot product, using spherical coordinates.
A spherical coordinate is a point represented by a radius , a primary angle “theta”, and a secondary angle “phi”. This is represented as a tuple:
The dot product of two spherical coordinates:
Because all normal vectors are unit length, and the light vector is unit length, we can just assume the radius is equal to 1. This simplifies to:
And using the previous variable names, we get the formula:
In the ROM, I decided to fix “L-theta” to a constant value for performance reasons. The player gets to control “L-phi”, creating an orbiting light effect.
This means that we can extract constant coefficients and and rewrite the formula:
The ROM encodes each pixel as a 3-byte tuple of .
Not only does the SM83 CPU not support multiplication, but it also doesn’t support floats. That’s a real bummer.
We have to get really creative when the entire mathematical foundation of this project involves multiplying non-integer numbers.
What do we do instead? We use logarithms and lookup tables!
Logarithms have this nice property of being able to factor products to outside the . This way, we can add values instead!
This requires two lookups: a log lookup, and a pow lookup.
In pseudocode, multiplying 0.3 and 0.5 looks like this:
pow = [ … ] # A 256-entry lookup table
# float_to_logspace() is compile-time. Accepts -1.0 to +1.0.
# x and y are 8-bit values in log-space
x = float_to_logspace(0.3)
y = float_to_logspace(0.5)
result = pow[x + y]
One limitation of this is that it’s not possible to take the log of a negative number. e.g. has no real solution.
We can overcome this by encoding a “sign” bit in the MSB of the log-space value. When adding two log-space values together, the sign bit is effectively XOR’d (toggled). We just need to ensure the remaining bits don’t overflow into it. We ensure this by keeping the remaining bits small enough.
The pow lookup accounts for this bit and returns a positive or negative result based on it.
It’s advantageous to restrict numbers to a single byte, for both run-time performance and ROM size. 8-bit fractions are pretty extreme by today’s standards, but believe it or not, it works. It’s lossy as hell, but it works!
All scalars we’re working with are between -1.0 and +1.0.
Addition and multiplication both use… addition!
Consider adding the two bytes: 5 + 10 = 15
Why is the denominator 127 instead of 128? It’s because I needed to represent both positive and negative 1. In a two’s-complement encoding, signed positive 128 doesn’t exist.
You might notice that the log-space values cycle and become negative at byte 128. The log-space values use bit 7 of the byte to encode the “sign” bit. As mentioned in the previous section, this is important for toggling the sign during multiplication.
The log-space values also use as a base, because I chose this as a sufficiently small base to meet the requirement that adding 3 of these log-space values won’t overflow (42+42+42 = 126). Bytes 43 thru 127 are near 0, so in practice the ROM doesn’t encode these values.
The lookup tables look like this:
Reconstructed functions look like this. The precision error is shown in the jagged “staircase” patterns:
It may look like there’s a lot of error, but it’s fast and it’s passable enough to look alright! ;)
It’s basically a combined . This exists because in practice, cosine is always used with a multiplication.
The core calculation for the shader is:
And we can rewrite it as:
The procedure processes 15 tiles per frame. It can process more if some of the tile’s rows are empty (all 0), but it’s guaranteed to process at least 15.
Figure: Mesen’s “Event Viewer” window, showing a dot for each iteration (tile row) of the shader’s critical loop.
There’s some intentional visual tearing as well. The image itself is more than 15 tiles, so the ROM actually switches to rendering different portions of the image for each frame. The tearing is less noticeable because of ghosting on the LCD display, so I thought it was acceptable.
A pixel takes about 130 cycles, and an empty row’s pixel takes about 3 cycles.
At one point I had calculated 15 tiles rendering at exactly 123,972 cycles, including the call and branch overhead. This is an overestimate now, because I since added an optimization for empty rows.
The Game Boy Color’s CPU runs up to 8.388608 MHz, or roughly 139,810 T-cycles per frame (1/60 of a second).
About 89% of a frame’s available CPU time goes to rendering the 15 tiles per frame. The remaining time goes to other functionality like responding to user input and performing hardware IO.
Figure: A hex representation of the shader subroutine instructions in RAM. The blue digits show a patch to change sub a, 0 into sub a, 8.
The core shader subroutine contains a hot path that processes about 960 pixels per frame. It’s really important to make this as fast as possible!
Self-modifying code is a super-effective way to make code fast. But most modern developers don’t do this anymore, and there are good reasons: It’s difficult, rarely portable, and it’s hard to do it right without introducing serious security vulnerabilities. Modern developers are spoiled by an abundance of processing power, super-scalar processors that take optimal paths, and modern JIT (Just-In-Time) runtimes that generate code on the fly. But we’re on the Game Boy, baybeee, so we don’t have those options.
If you’re a developer who uses higher-level languages like Python and JavaScript, the closest equivalent to self-modifying code is eval(). Think about how nervous eval() makes you feel. That’s almost exactly how native developers feel about modifying instructions.
On the Game Boy’s SM83 processor, it’s faster to add and subtract by a hard-coded number than it is to load that number from memory.
unsigned char Ltheta = 8;
// Slower
v = (*in++) - Ltheta;
// Faster
v = (*in++) - 8;
In SM83 assembly, this looks like:
; Slower: 28 cycles
ld a, [Ltheta] ; 12 cycles: Read variable “Ltheta” from HRAM
ld b, a ; 4 cycles: Move value to B register
ld a, [hl+] ; 8 cycles: Read from the HL pointer
sub a, b ; 4 cycles: A = A - B
; Faster: 16 cycles
ld a, [hl+] ; 8 cycles: Read from the HL pointer
sub a, 8 ; 8 cycles: A = A - 8
The faster way shaves off 12 cycles. If we’re rendering 960 pixels, this saves a total of 11,520 cycles. This doesn’t sound like a lot, but it’s roughly 10% of the shader’s runtime!
So how can we get the faster subtraction if the value we’re subtracting with changes?
2A ld a, [hl+]
D6 08 sub a, 8
“AI Will Be Writing 90% of Code in 3 to 6 Months”
— Dario Amodei, CEO of Anthropic (March 2025 - 9 months ago as of writing)
95% of this project was made by hand. Large language models struggle to write Game Boy assembly. I don’t blame them.
Update: 2026-02-03: I attempted to use AI to try out the process, mostly because 1) the industry won’t shut up about AI, and 2) I wanted a grounded opinion of it for novel projects, so I have a concrete and personal reference point when talking about it in the wild. At the end of the day, this is still a hobbyist project, so AI really isn’t the point! But still…
I believe in disclosing all attempts or actual uses of generative AI output, because I think it’s unethical to deceive people about the process of your work. Not doing so undermines trust, and amounts to disinformation or plagiarism. Disclosure also invites people who have disagreements to engage with the work, which they should be able to. I’m open to feedback, btw.
I’ll probably write something about my experiences with AI in the future.
As far as disclosures go, I used AI for:
Python: Reading OpenEXR layers, as part of a conversion script to read normal map data
Python/Blender: Some Python scripts for populating Blender scenes, to demo the process in Blender
SM83 assembly: Snippets for Game Boy Color features like double-speed and VRAM DMA. Unsurprising, because these are likely available somewhere else.
I attempted - and failed - to use AI for:
SM83 assembly: (Unused) Generating an initial revision of the shader code
I’ll also choose to disclose what I did NOT use AI for:
The algorithms, lookups, all other SM83 assembly
The soul 🌟 (AI techbros are groaning right now)
Just to see what it would do, I fed pseudocode into Claude Sonnet 4 (the industry claims that it’s the best AI model for coding in 2025), and got it to generate SM83 assembly:
It was an interesting process. To start, I chewed Claude’s food and gave it pseudocode, because I had a data format in mind, and I assumed it’d struggle with a higher-level description.
...
Read the original on blog.otterstack.com »
To add this web app to your iOS home screen tap the share button and select "Add to the Home Screen".
10HN is also available as an iOS App
If you visit 10HN only rarely, check out the the best articles from the past week.
If you like 10HN please leave feedback and share
Visit pancik.com for more.