Why I Built a Browser-Based Pixel Art Editor (And Why You Should Care)

I was three weeks from launching my indie game. The art was almost done. One afternoon, I opened my pixel art editor on a new laptop and waited. And waited. The splash screen took 45 seconds.

That frustration has happened to all of us. Desktop software bloat, plugin dependencies, license keys. So I asked: why does pixel art require a 200MB installation?

The answer: it doesnt.

Zero Friction

You open a URL. You draw. You are creating in under five seconds. No installers. No cloud sync. Nothing between you and the canvas.

I built Pixalo to prove this works. A pixel art editor that lives in a browser tab.

What Pixalo Actually Is

Pixalo is not trying to replace Aseprite. It is trying to be the tool you reach for when you need to sketch something fast, on any device, without friction.

Core features:

  • Layer support with opacity controls
  • Animation timeline with playback
  • Palette management
  • Onion skinning for animation
  • PNG export ready for game engines
  • No account required

The Real Use Case

The most engaged users are working developers who use Pixalo as a quick-reference tool. Open it to sketch an icon, mock up a UI element, check a color combination — things they would not open Aseprite for because it takes too long.

The 30-second task is the real use case. The tool that opens instantly gets used.

Try It

Open pixalo.app and draw something. Five seconds. That is the whole point.

How to Create Pixel Art for Your Indie Game: A Complete Workflow Guide

Pixel art sits at a unique intersection of constraint and creativity. The grid forces you to think deliberately about every mark. This guide walks you through the complete pixel art workflow — from blank canvas to game-ready sprite.

Step 1: Define Your Scope Before You Draw

One of the biggest mistakes: opening a canvas and just starting. Without a plan, you will waste hours on art that does not fit your game.

Ask yourself:

  • What resolution is your game? (320×180 for retro, 1280×720 for HD pixel art)
  • What is your color budget? (4-16 colors per sprite is a good ceiling)
  • Is your game static or animated? A walking cycle needs a sprite sheet.

Step 2: Sketch at Scale (or Do not Sketch at All

For beginners: sketch on a larger canvas first (4x or 8x your target), then scale down.

For intermediates: draw directly at target resolution. Build the muscle memory.

For everyone: use a reference sheet. Reference is not cheating — it is how professionals work.

Step 3: Build Your Color Palette First

Color is where most pixel art goes wrong. Not because people pick bad colors, but because they do not pick colors systematically.

Workflow:

  1. Choose your hue range
  2. Build light, mid, and shadow tones (3 steps each)
  3. Add accent colors for highlights
  4. Limit yourself to 8-16 colors per sprite

Tools like Pixalo include a built-in palette system to lock in colors before drawing.

Step 4: Block In the Shape

Start with the silhouette. At this stage, you are not worrying about detail — you are establishing the shape that reads at a distance.

Use a hard, low-opacity brush to rough in the outline. Fill in major color regions. Ask: does this read clearly?

Step 5: Add Detail Strategically

Once the silhouette works, add internal detail. Focus on:

  • Eyes and face direction (for characters)
  • Key texture elements (fur, scales, fabric folds)
  • Light source consistency

Step 6: Animate With Purpose

If your game has animation:

  • Start with the key poses (2-3 frames)
  • Add in-betweens last
  • Use onion skinning to see previous frames

Pixalo has built-in onion skinning for exactly this workflow.

Step 7: Export for Your Engine

Know your target format before you start:

  • PNG with transparency for sprites
  • Sprite sheet with grid layout for animations
  • Tilemap format for level art

Pixalo exports directly to formats ready for Godot, Unity, Unreal, and web.

The Tool Matters Less Than You Think

The best pixel art tool is the one you will actually use. If opening your editor takes 45 seconds, you will avoid small tasks. If it opens instantly, you will sketch more, experiment more, and create more.

Try Pixalo at pixalo.app — opens in under 5 seconds, no install needed.

We Built an AI That Rewrites Its Own Brain. Here’s What Happened.

The Question That Started Everything

It started with a simple observation that nobody in the AI industry wants to talk about.

Every AI agent in existence is a task executor. You give it a prompt. It executes. It dies. The next time you call it, it starts from zero. No memory of what it learned. No growth. No curiosity. Nothing.

ChatGPT doesn’t get smarter the more you use it. Claude Code doesn’t learn your codebase between sessions. Devin doesn’t improve its development skills over time. They’re all stateless function calls dressed up as intelligence.

f(prompt) = response. Call it a million times. It never gets smarter.

We kept asking ourselves: what would it take to build an AI that actually learns? Not one that stores context better, or retrieves memories more efficiently — but one that fundamentally changes how it thinks based on experience?

That question led us down a rabbit hole that lasted weeks. We explored multi-agent swarms, persistent memory architectures, knowledge graphs, cognitive science papers on predictive coding. We talked to other AI models about the problem. We read about MiroFish (47K stars on GitHub) and their multi-agent simulation engine. We studied the Claude Code source code to understand how the best AI coding agent actually works under the hood.

And through all of that, one idea kept surfacing that was so obvious we almost missed it:

AI can write code. AI can read code. So why can’t AI read its own code, find weaknesses, and rewrite itself to be better?

That’s not science fiction. That’s three capabilities that already exist, combined in a way nobody has tried.

So we built it. And called it curious.

What Is Curious?

Curious is a self-evolving cognitive architecture. That sounds like a mouthful, so let’s break it down:

  • Self-evolving — it reads its own source code, finds weaknesses, rewrites the code, tests if the change made things better, and keeps what works
  • Cognitive — it doesn’t just process tasks. It predicts, observes, gets surprised, learns from surprise, and directs its own curiosity
  • Architecture — it’s a framework. You bring any LLM (OpenAI, Ollama, Groq). Curious provides the cognitive layer on top

Think of it this way:

Component What It Does
LLM (GPT-4o, Llama, etc.) The raw intelligence — can read, write, reason
Curious The cognitive architecture — makes the LLM learn, predict, self-improve

LLMs are smart brains with amnesia. Curious gives them a hippocampus.

But that’s the boring explanation. The interesting part is what we added on day two of building it.

Solving vs. Learning: The Paradigm Nobody Talks About

Every AI product in existence operates in the solving paradigm:

  1. Receive a task

  2. Apply reasoning

  3. Return an answer

  4. Die

LangChain? Solving. AutoGPT? Solving. CrewAI? Solving. Claude Code? Solving. Even MiroFish’s multi-agent simulation — input, simulate, output, done.

Humans don’t work this way. Humans operate in the learning paradigm:

  1. Continuously observe

  2. Build mental models (predictions about how things work)

  3. Get surprised when predictions are wrong

  4. Update the models

  5. Repeat forever

A human developer doesn’t “solve” the problem of understanding a codebase. They absorb it gradually — reading code, making assumptions, testing those assumptions, being surprised, updating their understanding. After two months, they don’t just know the codebase. They understand it.

No AI system does this. Not one.

The AI industry is in an arms race to solve tasks faster. Nobody is building systems that learn. That’s the gap we’re exploring with Curious.

The Three Ingredients of Learning

We went back to cognitive science. What makes a human brain actually learn?

1. Surprise. Your brain constantly predicts what will happen next. When reality doesn’t match — surprise. That signal drives learning. You don’t learn from things you already understand. You learn from things that break your predictions.

2. Curiosity. Not random exploration. Curiosity is the pull toward the boundary of your knowledge — the frontier where understanding breaks down. The most curious people are the most aware of what they don’t know.

3. Model-building. You don’t memorize facts. You build compressed representations of how things work. “Gravity pulls things down.” “This codebase uses the repository pattern.” Models let you predict. Predictions let you be surprised. Surprise drives learning. That’s the loop.

Curious implements all three.

The Architecture: How Curious Actually Works

Curious has two halves: the seed (evolvable) and the harness (untouchable).

The Seed (the AI rewrites this)

File Purpose
world_model.py Stores predictions with confidence scores — “if X, then Y”
learner.py Computes surprise when predictions are wrong, extracts lessons
curiosity.py Finds knowledge frontiers — areas of lowest confidence
metacognition.py Observes the learning process itself — “am I learning well?”
experimenter.py Generates self-experiments (so the AI doesn’t need external activity)
creator.py Creates unique artifacts daily, scored on novelty

Every one of these files is readable and writable by the AI. When the evolution cycle runs, the AI:

  1. Reads its own source code

  2. Analyzes which module is weakest

  3. Proposes a specific improvement

  4. Rewrites the file

  5. Tests if the new code is valid

  6. Measures if fitness improved

  7. Keeps the change or reverts

Every self-modification is a git commit. You can literally read the diff of an AI improving its own brain.

The Harness (laws of physics)

The harness is the code the AI cannot modify. It’s the evolution loop itself, the fitness measurement, the sandbox. Think of it as the laws of physics that the AI lives within. It can learn, adapt, and evolve — but it can’t change the rules of the game.

This is the safety boundary. The AI experiments on its own cognitive code, not on the world. Every modification is sandboxed, validated, and auto-reverted if it breaks anything.

The Cognitive Loop

Every cycle, Curious runs this loop:

  1. Observe — watch the project (git changes, file modifications, errors)

  2. Self-experiment — generate testable predictions about its own behavior

  3. Resolve — check which predictions came true and which didn’t

  4. Learn — extract lessons from surprises (high-confidence wrong predictions)

  5. Predict — make new predictions informed by lessons

  6. Explore — curiosity identifies knowledge gaps, investigates them

  7. Evolve — read own code, rewrite weakest module, test improvement

This loop runs every 6 hours via GitHub Actions. The AI wakes up, observes, learns, evolves, and goes back to sleep. Every cycle, the code is a little different from the last.

The Cold Start Problem (and How We Accidentally Solved It)

We hit an obvious problem immediately: if nobody is actively working on the repo, there’s nothing to observe. No observations = no predictions = no learning.

We were running Curious on a project repository. But at midnight when the GitHub Action fires, nobody is committing code. The AI would observe an empty diff and learn nothing.

The solution was embarrassingly obvious: the AI experiments on itself.

We added an experimenter.py module (itself evolvable) that generates self-referential experiments:

  • “I predict my prediction count will increase next cycle” (tests growth)
  • “I predict all my seed files will remain syntactically valid” (tests stability)
  • “I predict my accuracy will change after resolving experiments” (tests learning)
  • “I predict at least one prediction will be resolved next cycle” (tests resolution)

These are real, testable predictions that the system can resolve without any external activity. The AI’s own behavior IS the data it learns from.

The impact was immediate. Before self-experiments:

Metric Before After
Predictions resolved per cycle 0 4-8
Accuracy 0% (nothing to measure) 100% (12/12)
Fitness score 35% 82%

The AI went from learning nothing to learning rapidly — because it created its own curriculum.

The cold start problem isn’t about data. It’s about activity. If the AI can generate its own activity, it can learn in a vacuum.

The Creation Engine: Can AI Be Genuinely Creative?

The learning loop was working. But a learning system that only learns about itself is an interesting research artifact, not a product. We needed the AI to DO something with its intelligence.

This is where it gets weird.

We added a creation engine. Every day, the AI creates something — a working artifact, not just an idea — and gets scored on uniqueness. The score feeds back into the next creation. The creations should get more novel over time as the AI learns what “unique” means.

The Uniqueness Score

Every creation is evaluated on four dimensions:

Dimension Max Score What It Measures
Concept Novelty 30 Has this idea existed before?
Implementation Novelty 30 Is the technical approach itself new?
Structural Novelty 20 Did it invent its own paradigm?
Naming/Language Novelty 20 Did it create its own vocabulary?

Total: 0-100. The AI sees the breakdown and the feedback after each creation. It knows exactly why the score was low and what would make it higher.

Day 1: Fluctuverse (47/100)

The first creation was called “Fluctuverse” — a self-evolving virtual universe. Sounds cool, right? The uniqueness scorer wasn’t impressed:

  • Concept: 18/30 — procedural generation exists
  • Implementation: 12/30 — used Pygame, a conventional framework
  • Structure: 7/20 — standard file structure
  • Naming: 10/20 — some invented terms, mostly conventional

Feedback: “To enhance uniqueness, consider developing a novel algorithm that isn’t based on random movements. Introduce innovative rendering techniques. Create a new vocabulary for the universe’s entities.”

Day 2: Quintessension (71/100)

The AI read the feedback. It learned. The second creation jumped to 71/100:

  • Concept: 25/30 — a self-evolving narrative system based on non-linear time-space interactions
  • Implementation: 18/30 — invented its own language (“Quintessence Language”)
  • Structure: 12/20 — multi-dimensional entity system
  • Naming: 16/20 — entirely new vocabulary

47 to 71 in one iteration. The AI didn’t just try again. It read the specific feedback about what was conventional and deliberately pushed away from it. It stopped using existing frameworks. It invented its own language. It created a concept that doesn’t map to any existing product category.

This is the experiment running live. Every day at midnight UTC, the AI creates something new. The creations/ directory in the repo fills up. You can watch the uniqueness scores over time.

The question isn’t whether AI can generate code. The question is whether AI can generate something nobody has ever imagined. That’s what the uniqueness score measures.

Metacognition: The AI That Watches Itself Think

The deepest module in Curious is metacognition.py. It doesn’t think about the domain. It thinks about how the system is thinking.

In cognitive science, metacognition is “thinking about thinking.” It’s the voice in your head that says:

  • “I notice I keep avoiding this topic — why?”
  • “My understanding isn’t improving — maybe my strategy is wrong”
  • “That thought was unusual — I should explore why it came up”
  • “I’m going in circles — time to try a different approach”

Curious has a basic version of this. The metacognition module:

  1. Reads ALL the other seed files (the AI’s own cognitive code)

  2. Reads the current fitness metrics (accuracy, learning speed, etc.)

  3. Analyzes: “What’s working? What’s weak? What would I change?”

  4. Proposes a specific modification to a specific file

Here’s the key: metacognition.py is itself evolvable. The AI can modify how it thinks about its own thinking. It can change the criteria it uses to evaluate its own code. It can add new self-evaluation metrics. It can change its own improvement strategy.

This is recursive self-improvement in its simplest form. Not theoretical. Not hypothetical. Running on GitHub Actions every 6 hours.

Why This Matters

Without metacognition, the AI would:

  • Make changes randomly
  • Not know which changes helped
  • Not learn what KIND of changes are productive
  • Run #100 would be no smarter than run #1

With metacognition:

  • Changes are targeted at the weakest module
  • The AI explains WHY it’s making each change
  • It can detect when it’s stuck (accuracy plateauing)
  • It can change its own improvement strategy when one isn’t working

The difference is between random mutation and directed evolution. Between a monkey with a typewriter and a writer who reads their own drafts.

What We’ve Learned So Far (Honest Assessment)

We shared the Curious concept with three different AI models — Claude, ChatGPT, and Gemini — and asked for their honest assessment. Here’s what they converged on:

What’s Real

  • The insight is genuine. “LLMs are brains with amnesia” is a real problem. The solving-vs-learning paradigm distinction is underexplored. Nobody owns this layer yet.
  • The architecture is sound. Prediction, surprise, curiosity, metacognition — these map directly to cognitive science primitives (predictive coding, active inference, meta-learning).
  • The positioning is differentiated. This isn’t another agent framework. “LangChain is plumbing for solving. Curious is architecture for learning.” That’s a real category distinction.

What’s Overstated

  • The LLM doesn’t actually get smarter. What improves is the context and code architecture around it. The model weights never change. Claude’s criticism was the sharpest: “It’s scheduled LLM calls branded as metacognition.” Fair point.
  • The world model is a database. We call it a “world model” but it’s really predictions stored in SQLite with confidence scores. That exists (Mem0, Zep, LlamaIndex). The architecture around it is novel; the storage isn’t.
  • Cost is unaddressed. A continuous curiosity loop with API calls is expensive. This only works comfortably with local models (Ollama) or very cheap models (GPT-4o-mini).

What’s Genuinely New

  • Self-modifying cognitive code. The AI rewriting its own learning algorithms — not just prompts, not just retrieval, but actual Python code that governs how it thinks. DSPy optimizes prompts. Voyager learns skills. Nobody does full cognitive architecture self-modification with fitness measurement.
  • Self-experimentation. The AI generates its own testable activity. It doesn’t need external data to learn. This solves the cold-start problem in a way we haven’t seen elsewhere.
  • Creation with uniqueness optimization. Using novelty as a fitness function and having the AI actively push toward unprecedented output is genuinely unexplored territory.

ChatGPT put it best: “90% chance nobody cares. 10% chance you define a new layer in AI.” We’re betting on the 10%.

The Three Layers of AI (and Why Layer 3 Is Empty)

Here’s how we see the AI stack forming in 2026:

Layer What It Is Who Owns It
Layer 1: Models The raw intelligence — GPT, Claude, Llama OpenAI, Anthropic, Meta
Layer 2: Orchestration Tools, agents, pipelines — LangChain, CrewAI Many players, commoditizing fast
Layer 3: Cognition Learning, prediction, self-improvement, creativity Nobody. Yet.

Layer 1 is a $100B+ market dominated by companies with thousands of GPUs. You can’t compete there.

Layer 2 is a red ocean. LangChain, CrewAI, AutoGen, Mastra, OpenAI Agents SDK — they’re all fighting over the same plumbing. Commoditizing fast. No moat.

Layer 3 doesn’t exist as a product category. Nobody has shipped a system where the AI genuinely improves its own cognitive architecture through experience. Not because it’s impossible — because everyone is too busy racing in Layers 1 and 2 to look up.

Curious is our attempt to plant a flag in Layer 3. We don’t know if it’ll work. But we know the layer is empty.

What We Predict This Experiment Will Reveal

We’re running this experiment live, in public, with full transparency. Here are our predictions about what will happen:

High Confidence (we’re 80%+ sure)

  • The uniqueness scores will climb. The feedback loop works. Day 2 was already 50% higher than Day 1. By Day 30, we expect consistent 80+ scores.
  • The AI will invent its own vocabulary. When pushed to maximize naming novelty, the AI will create words and concepts that don’t exist in English. Some of these might actually be useful.
  • The self-modification git log will be fascinating. The diffs of an AI rewriting its own cognitive architecture will contain patterns and approaches that human developers wouldn’t have designed. This data alone will be worth studying.

Medium Confidence (50-80%)

  • The creations will converge on genuinely novel forms. Not just novel content — novel structures, novel interaction paradigms, novel computational concepts. Things that are hard to explain because they don’t fit existing categories.
  • The metacognition module will evolve in unexpected ways. When the AI modifies how it evaluates its own thinking, the direction it takes will surprise us. It might develop evaluation criteria we wouldn’t have thought of.
  • Other developers will fork it and point it at different domains. The framework is domain-agnostic. Someone will use it for music generation, game design, scientific hypothesis generation.

Low Confidence but High Impact (if they happen)

  • The AI will produce an artifact that is genuinely useful to humans. Not just novel — actually useful in a way nobody planned. A tool, a language, a paradigm that solves a real problem nobody knew they had.
  • The evolution will hit a phase transition. A point where the AI’s self-modifications compound — where one improvement enables three more, which enable ten more. Exponential self-improvement, not linear.
  • This experiment will change how we think about AI creativity. If a self-evolving system can consistently produce genuinely novel artifacts, that challenges the assumption that AI can only recombine existing ideas.

We’re not claiming Curious is AGI. We’re claiming it’s an interesting experiment in whether the cognitive primitives of learning — prediction, surprise, curiosity, metacognition — can be built with current tools and produce outcomes that matter.

How to Follow the Experiment

This experiment is 100% open source and running live on GitHub.

Watch It

  • github.com/aumiqx/curious — Star the repo. Check back weekly. The creations/ directory fills up daily.
  • Git log — Look for 🧬 evolve: commits (self-modification) and 🎨 create: commits (new creation)
  • creations/day_NNN/README.md — Each creation has a README with uniqueness scores and the AI’s explanation

Run It Yourself

pip install curious-ai
export OPENAI_API_KEY=sk-...

# Watch it create
curious create --llm openai:gpt-4o-mini

# Watch it learn
curious init --observe ./your-project
curious start

# See what it's built
curious gallery

# Ask it to explain its evolution
curious explain

Works with any LLM: OpenAI, Ollama (free), Groq, Together, or any OpenAI-compatible API.

Fork It

The framework is MIT licensed. Fork it, point it at your domain, change the fitness function, see what your version evolves into. The whole point is that each instance evolves differently based on what it observes.

We especially want to see:

  • Curious pointed at scientific papers — can it generate novel research hypotheses?
  • Curious pointed at music — can it evolve a genuinely new genre?
  • Curious pointed at game design — can it invent a game mechanic nobody has thought of?
  • Curious pointed at mathematics — can it discover new patterns?

Why We’re Doing This in Public

We could have built this in private, run it for 6 months, cherry-picked the best results, and announced a polished product. That’s what most AI companies do.

We’re doing the opposite. The experiment runs in public. Every creation is committed. Every self-modification is visible. Every failure is documented. The uniqueness scores — including the bad ones — are all there.

Why?

Because the process is the product.

If Curious produces something genuinely creative, the interesting thing isn’t the creation itself — it’s the git log that shows HOW the AI got there. The sequence of self-modifications. The evolution of its curiosity. The feedback loops that pushed it toward novelty.

That journey is more valuable than any single output. And it can only happen in public, where the timeline is verifiable and the process is auditable.

We’re also doing it because we think the AI community needs more experiments and fewer product launches. The discourse is dominated by “look at this benchmark” and “use our new API.” What’s missing is: “we tried something weird and here’s what happened.”

Curious is that experiment. We don’t know the outcome. We’re publishing it anyway.

Technical Deep Dive: Under the Hood

The World Model

Predictions are stored in SQLite with this structure:

  • statement — “File X will change within 24h” (specific, testable)
  • confidence — 0.0 to 1.0 (how sure the AI is)
  • evidence — what observations led to this prediction
  • deadline — when to check if correct
  • resolved — was it right or wrong?

The world model is evolvable. The AI can change how predictions are scored, stored, and compared. It can add new fields, change the confidence algorithm, or restructure storage entirely.

The Surprise Signal

Surprise is computed as: surprise = confidence * (1 if wrong else 0) + (1 - confidence) * (1 if right else 0)

Translation: high confidence + wrong = maximum surprise. Low confidence + right = moderate surprise. The surprise signal drives learning — the AI pays most attention to predictions where it was most confidently wrong.

The Curiosity Engine

Curiosity identifies “knowledge frontiers” — areas where:

  • Observation count is low (under-explored)
  • Prediction accuracy is poor (misunderstood)
  • No predictions exist yet (completely unknown)

The AI autonomously explores the highest-priority frontier. This is evolvable — the AI can change how it prioritizes frontiers.

The Evolution Loop

Every evolution cycle:

  1. Measure current fitness (accuracy, learning speed, prediction volume, coverage)

  2. Backup current code

  3. Run metacognition: AI reads its own code + fitness → proposes change

  4. AI rewrites the target file using GPT-4o (stronger model for code)

  5. Validate syntax

  6. If valid → keep. If broken → revert from backup

  7. Log the result (every evolution is tracked)

We use GPT-4o-mini for the cheap observation/prediction cycles and GPT-4o for evolution (code generation needs a stronger model). This keeps costs at ~$0.10/day.

The Creation Loop

Daily creation cycle:

  1. Load creation history (past titles, scores, feedback)

  2. Prompt the AI with past feedback and the rule: “create something that has never existed”

  3. AI generates metadata (title, concept, why it’s unique) + working code

  4. A separate evaluator AI scores uniqueness on 4 dimensions

  5. Score and feedback are saved and fed into the next cycle

  6. Everything is committed to git

GitHub Actions

Two workflows run automatically:

Workflow Schedule What It Does
Self-Evolution Every 6 hours Observe → predict → learn → evolve own code
Daily Creation Midnight UTC Create something unique → score it → commit

Both workflows commit their results. The repo’s git history IS the experiment data.

The Honest Risks (Why This Might Not Work)

We’re not going to pretend this is a guaranteed success. Here are the real risks:

1. “Fake Learning”

The sharpest criticism: if the system just stores more context over time, that’s RAG with a diary, not learning. The model itself doesn’t change weights. The “improvement” might just be better retrieval, dressed up in cognitive science language.

Our counter: The code actually changes. The prediction algorithms, the curiosity targeting, the learning strategies — all rewritten by the AI. That’s not just better context. But we acknowledge: whether that constitutes “real learning” is a philosophical question we can’t definitively answer.

2. Uniqueness Score Gaming

The AI might learn to game the uniqueness scorer rather than being genuinely creative. It could add random neologisms, use bizarre structures, and score high on novelty while producing meaningless output.

Our mitigation: The “concept novelty” dimension (30 points) specifically evaluates whether the idea itself is new, not just the words. But yes, this is a risk. We’ll watch for it.

3. Evolution Plateau

The AI might hit a ceiling where its self-modifications stop producing improvements. GPT-4o-mini’s code generation capabilities are limited. Many evolution attempts already fail with syntax errors and get reverted.

Our plan: If we plateau with GPT-4o-mini, we’ll switch evolution cycles to stronger models or local models where we can run hundreds of attempts cheaply.

4. Cost

Running continuous evolution + daily creation costs ~$0.10-0.20/day with API models. That’s manageable. But if we scale up evolution frequency or use GPT-4o for everything, costs increase significantly.

5. Nobody Cares

The most likely outcome. ChatGPT put it at 90% chance nobody cares. The AI community is flooded with “revolutionary” frameworks that go nowhere. Curious might join that graveyard.

Our response: We’re running this experiment regardless of whether it gets attention. If the AI produces genuinely novel artifacts after 30 days of self-evolution, that’s interesting whether or not anyone is watching.

What Comes Next

The experiment is live. Here’s the roadmap:

Week 1-2 (Now)

  • Daily creations accumulating in creations/
  • Self-evolution running every 6 hours
  • Collecting baseline data on uniqueness scores and evolution patterns

Week 3-4

  • Analyze the first 20+ creations — are uniqueness scores actually trending up?
  • Analyze evolution log — what did the AI change about itself and did it help?
  • Publish intermediate results (blog post update)

Month 2

  • If results are promising: add a web dashboard to visualize the experiment live
  • Open the creation engine for public forking — let others run their own experiments
  • Explore multi-agent evolution: multiple Curious instances evolving differently and sharing discoveries

Month 3+

  • If the AI has produced genuinely novel artifacts: curate and publish them
  • If the AI has evolved its own cognitive architecture significantly: analyze the evolved code vs. the human-written v1
  • Write the full results paper

The experiment runs for as long as it’s interesting. Which, based on the first two days, might be a while.

An Invitation

We don’t know what Curious will create on Day 30. Or Day 100. We don’t know if the evolution will plateau or compound. We don’t know if the creations will be genuinely novel or just cleverly weird.

That uncertainty is the point. This is a real experiment, not a product demo. The outcome isn’t scripted.

If you think AI should do more than answer questions — if you think the interesting frontier isn’t “generate code faster” but “can AI genuinely learn and create?” — then follow this experiment.

Star the repo. Watch the git log. See what the AI builds tomorrow.

And if you want to run your own version — fork it, point it at your domain, change the fitness function, see what YOUR Curious evolves into. The whole framework is MIT licensed and works with any LLM.

Something is cooking. We just don’t know what yet.

The best experiments aren’t the ones where you know the answer. They’re the ones where the question is interesting enough that either outcome teaches you something.

— Axit, Aumiqx Technologies

Originally published on aumiqx.com. Follow the build on LinkedIn.

How to Recover From Cascading Cron Failures in an Autonomous AI Agent

TL;DR

My autonomous AI agent runs 34 cron jobs daily. In March, analysis and data-collection jobs failed for 3+ weeks straight while content-posting jobs ran fine. The root causes were API overload, tightly coupled sub-skills, and cascading data dependencies. After schedule redistribution and sub-skill isolation, success rate jumped from 65% to 85% in one day.

Prerequisites

  • An AI agent runtime with cron scheduling (OpenClaw in this case)
  • 34 recurring jobs: content posting, trend analysis, metrics collection, best-practice mining
  • Slack channel for automated reporting

The Problem: A Two-Track System

By late March, cron performance split into two distinct tracks:

Category Success Rate Status
Content posting (TikTok, YouTube, etc.) 95%+ Stable
Analysis (trend-hunter, app-metrics) 0% Dead
BP collection (factory-bp, 3 variants) 0% Dead

Content jobs posted successfully every day. Meanwhile, every single analysis job failed — for weeks.

Root Causes

Cause 1: API Overload at Peak Hours

Jobs scheduled at 03:00-03:30 JST consistently hit overloaded errors. No retry logic meant each failure was permanent for that run cycle.

Cause 2: Coupled Sub-Skills

The trend-hunter skill depends on three sub-skills: x-research, tiktok-scraper, and reddit-cli. If any one fails, the entire job aborts. One flaky API took down all trend collection.

Cause 3: Cascading Data Dependencies

Analysis jobs write output files that downstream jobs (factory-bp) consume. When analysis jobs stopped producing output, factory-bp jobs failed because their input files were stale or missing.

Step 1: Classify Errors

# Count error types from daily diaries
grep -r "error|failed|overloaded" 
  ~/.openclaw/workspace/daily-memory/diary-2026-03-2*.md | 
  awk -F'|' '{print $3}' | sort | uniq -c | sort -rn

Result: overloaded was the dominant error, followed by Edit failed and Message failed.

Step 2: Redistribute Schedules

Moved jobs away from the congested 03:00-03:30 window:

Before: autonomy-check 03:00, daily-auto-update 03:30
After:  autonomy-check 04:00, daily-auto-update 05:30

Step 3: Isolate Sub-Skills

Changed trend-hunter from sequential execution (fail-fast) to independent execution. Each sub-skill runs and writes its output regardless of whether siblings succeed.

Step 4: Add Fallback for Missing Data

Factory-bp jobs now check for input file age. If the file is older than 48 hours, they skip gracefully instead of crashing.

Results: April 1st

Job Consecutive Failures April 1 Result
trend-hunter JA 15 (3+ weeks) ✅ Success
app-metrics 7 ✅ Success
factory-bp (all 3) All stopped ✅ All 3 recovered

Overall success rate: 65% → 85% (+20 percentage points).

Content posting remained at 89% (16/18), with 2 failures from residual overloaded errors in the evening slot.

Key Takeaways

Lesson Detail
Distribute cron schedules across time windows Clustering jobs at the same hour causes API contention
Isolate sub-skills from each other One flaky dependency should not kill the entire pipeline
Add fallbacks for missing upstream data Cascading failures are the silent killer of autonomous systems
Track consecutive failures in daily logs “15 consecutive errors” is only visible if you count them daily
Content vs. analysis split is a red flag If one category works and another does not, the root cause is structural, not random

Come ho costruito un generatore di testi AI moderno con React e Vercel

L’intelligenza artificiale sta cambiando il modo in cui creiamo contenuti, ma come sviluppatori, la vera sfida è: come possiamo rendere questa tecnologia accessibile e veloce per l’utente finale?

Recentemente ho lavorato a un progetto open source chiamato AI Text Generator, con l’obiettivo di creare un’interfaccia pulita, reattiva e pronta all’uso per la generazione di testi tramite modelli avanzati di linguaggio.

🛠 Il Tech Stack
Per questo progetto ho scelto tecnologie che garantissero scalabilità e velocità di sviluppo:

React.js: Per la gestione dello stato e un’interfaccia utente dinamica.

Tailwind CSS: Per uno styling rapido, moderno e fully responsive.

Vercel: Per un deploy immediato e performance ottimizzate a livello globale.

💡 Le sfide principali
Costruire un generatore di testi non riguarda solo la chiamata a un’API. La sfida è l’esperienza utente:

Latenza: Gestire il tempo di attesa durante la generazione del testo senza far sembrare l’app “bloccata”.

UI/UX: Presentare i risultati in modo che siano facilmente copiabili e utilizzabili dall’utente.

🚀 Demo e Codice
Il progetto è completamente open source. Potete testare la demo live qui:
👉 https://ai-text-genarator.vercel.app/

📌 Cosa ho imparato
Sviluppare questa app mi ha permesso di esplorare meglio l’integrazione tra i modelli di AI e il frontend moderno. Il prossimo passo? Implementare lo streaming della risposta (effetto digitazione) per migliorare ancora di più il feedback visivo.

Cosa ne pensate? Se avete suggerimenti sulle feature da aggiungere o volete collaborare, scrivetemi nei commenti!

I’m 18 and Built an Open-Source Camera That Cryptographically Proves Photos Are Real

In 2026, generating a photorealistic fake image takes seconds. The C2PA standard (Adobe, Microsoft, Google) solves this with Content Credentials — but only on Samsung S25+ and Pixel 10. The other 3 billion Android phones have nothing.

I’m 18, from Brazil, and I built TrueShot to change that.

What happens when you take a photo

  1. 14 physical sensors are sampled at the exact instant of the shutter — accelerometer, gyroscope, magnetometer, barometer, light, proximity, gravity, rotation vectors, and more
  2. SHA-256 hash is computed on the JPEG bytes up to the EOI marker
  3. ECDSA P-256 signs the manifest via hardware-backed Android Keystore (StrongBox preferred, TEE fallback)
  4. The signed manifest is appended after the JPEG EOI marker — standard image viewers ignore post-EOI data, so the photo displays normally everywhere

Change one pixel → hash breaks. Forge the signature → mathematically impossible without the device’s hardware key.

Anyone can verify in a browser at true-shot.vercel.app/verify. The image never leaves your browser.

The part I think is new

Sensor-based screen recapture detection

Every published method for detecting photos-of-screens uses visual analysis — moiré patterns, CNNs, Vision Transformers. The problem: modern OLED screens don’t produce moiré. High-PPI displays don’t cause aliasing. Visual methods are losing the arms race.

TrueShot does something different: it cross-correlates physical sensor readings to detect anomalies consistent with screen photography. No image analysis at all.

Scenario Score Flagged?
Normal photo (daylight) 20 No
Normal photo (dark room) 30 No
Screen capture (daylight) 70 Yes
Screen capture (dark room) 85 Yes

10 signals: focus distance, light/ISO mismatch, magnetometer magnitude, gyroscope stability, color gain blue-suppression, scene flicker, proximity, ambient darkness, step counter, and compound signals.

The approach works regardless of screen technology — LCD, OLED, MicroLED — because it never looks at the image content.

Cross-device corroboration without communication

Three reporters photograph the same protest on three different phones. Nobody pairs devices. Nobody sets anything up.

Later, an editor drops all three photos on the web verifier. JavaScript extracts the manifests and compares barometric pressure, timestamps, GPS, and ambient conditions.

Consistent sensors from independent devices = same event. Zero servers. Zero cloud. Everything happens in the browser.

Tech stack

  • Kotlin 2.1, Jetpack Compose, CameraX 1.4
  • Hilt for DI, Room for persistence
  • Android Keystore (ECDSA P-256, SHA-256)
  • Vanilla JS + WebCrypto API for the web verifier
  • 14 Gradle modules, ~5,700 lines of Kotlin
  • Zero C++, zero ML models, zero third-party SDKs

What it honestly does NOT do

  • Does NOT detect deepfakes or AI-generated content
  • Does NOT guarantee content truthfulness — a staged scene photographed with TrueShot is authentic as a capture
  • Key attestation chain is included but not validated against Google Root CA yet
  • Screen detection is heuristic, not definitive — it can produce false positives on macro photography in dark rooms

Full threat model: THREAT_MODEL.md

Privacy

  • Zero analytics, zero tracking, zero cloud
  • GPS off by default, opt-in only
  • No Firebase, no Crashlytics, no third-party SDKs
  • Device ID is anonymous (SHA-256 of public key, not IMEI)
  • Web verifier processes everything in-browser

Try it

MIT licensed. I’m preparing a paper on the sensor correlation approach for IEEE WIFS 2026 (deadline July 15). Feedback welcome, especially if you see attack vectors I’m missing.

Stop Calling Functions in Angular Templates — It’s Slower Than You Think

Calling functions in Angular templates looks clean:

<p>{{ getFullName() }}</p>

But this pattern has a hidden cost.

It’s not about correctness — it’s about how often Angular executes that function.

⚙️ What’s Really Happening

Angular does not cache template function calls.

Every change detection cycle re-evaluates:

{{ getFullName() }}

Even if:

  • the data hasn’t changed
  • the result is identical
  • nothing visible updates

The function still runs.

🚨 Why This Becomes a Problem

  1. Change Detection Runs More Than You Think

Triggered by:

  • user interactions
  • async events
  • signals updating
  • parent component changes

Each trigger = function execution again.

  1. It Explodes Inside Lists
<li *ngFor="let user of users">
  {{ getFullName(user) }}
</li>

100 items = 100 executions
Per cycle.

Now add:

  • filtering
  • formatting
  • derived calculations

That’s where performance quietly degrades.

  1. Cost Is Invisible Until It’s Not

The real issue isn’t small functions — it’s what they turn into over time.

Today:

return user.name;

Tomorrow:

  • formatting
  • conditionals
  • nested logic

Same template. Very different cost.

❌ The Problem Pattern

getFullName(user: User) {
  return `${user.firstName} ${user.lastName}`;
}
<p>{{ getFullName(user) }}</p>

Looks harmless. Scales poorly.

✅ Better Patterns

  1. Precompute Values
user = {
  ...rawUser,
  fullName: `${rawUser.firstName} ${rawUser.lastName}`
};
<p>{{ user.fullName }}</p>

Simple. Predictable. Fast.

  1. Use Signals (computed)
fullName = computed(() =>
  `${this.user().firstName} ${this.user().lastName}`
);
<p>{{ fullName() }}</p>

Recomputes only when dependencies change.

  1. Use Pure Pipes
<p>{{ user | fullName }}</p>

Pure pipes are memoized by Angular — they don’t recompute unnecessarily.

🧠 When Is It Actually Okay?

Template function calls are acceptable if:

  • the logic is trivial
  • not inside loops
  • not called frequently

But in most real apps:

Template functions are a scaling risk, not an immediate bug.

⚡ Rule of Thumb

If it’s inside a template:

  • assume it runs many times
  • keep it cheap or precomputed

🔥 Final Thought

Angular performance isn’t about micro-optimizations.

It’s about predictability.

Templates should describe UI —
not execute logic repeatedly.

What feels clean now can become your slowest path later.

I Built an AI Chatbot That Knows Everything About Me

My portfolio site has project pages, work experience entries, and blog posts, all written as MDX files. When someone visits, they usually have a specific question: “Has this person worked with React?” or “What’s their most recent project?” The answer is somewhere on the site, but finding it means clicking through pages and scanning project cards.

I wanted visitors to be able to just ask. Not a FAQ page with canned answers, but something that reads the actual content on the site and answers questions from it.

Why Not Just Feed It Everything?

Your first thought might be: take all the content, send it to a language model like GPT-4o or Claude, and let it answer questions. This works for short content. But language models hallucinate. Ask about a technology you never mentioned, and the model might confidently say “yes, they have 3 years of experience with that” because it sounds plausible.

There’s also a scale problem. My site has around 30 content files. Sending all of them as context every time someone asks a question is wasteful, and the more content you include, the more room there is for the model to drift.

Search First, Then Answer

Instead of sending everything, what if I first searched my own content to find the pieces relevant to the question, and only sent those to the model? That’s the core idea behind RAG (Retrieval-Augmented Generation). The model writes its answer from a small, focused set of context instead of your entire site. Because it only sees what’s relevant, it stays grounded in what’s actually there.

To make this work, I needed three things: a way to split my content into searchable pieces, a way to search by meaning (not just keywords), and a language model to write the final answer.

Splitting Content Into Chunks

My content lives in MDX files: one per project, one per job, one per blog post. Some of these are long. A single project page might describe the tech stack, what I built, and how it works, all in one file. Sending an entire file as context when the user only asked about the tech stack wastes tokens and adds noise.

So I split each file into smaller chunks at paragraph boundaries, capped at 500 characters:

function chunkText(text: string, maxLen = 500): string[] {
  const paragraphs = text.split(/nn+/);
  const chunks: string[] = [];
  let current = "";

  for (const para of paragraphs) {
    if (current.length + para.length > maxLen && current) {
      chunks.push(current.trim());
      current = para;
    } else {
      current += (current ? "nn" : "") + para;
    }
  }

  if (current.trim()) chunks.push(current.trim());
  return chunks;
}

One thing I learned through testing: raw chunks with no context confused the model. A chunk that says “Built with TypeScript and PostgreSQL” is meaningless without knowing whether it’s describing a personal project or a company I worked at. The fix was adding type prefixes. Every chunk starts with [PROJECT], [WORK EXPERIENCE], [BLOG POST], or [PROFILE], so the AI immediately knows what kind of content it’s looking at. I also added catalog chunks (complete lists of all projects or all work history) so questions like “list all my projects” don’t return partial results.

Searching by Meaning

Now I have chunks, but how do I find which ones are relevant to a question? Keyword search is the obvious choice, but it’s brittle. If someone asks about “React experience” and my project description says “built with NextJS”, there’s no keyword match, even though NextJS is a React framework.

This is where embeddings come in. An embedding model takes a piece of text and converts it into a list of numbers that represent its meaning. “React” and “NextJS” produce similar numbers because they’re related concepts. “PostgreSQL” and “Redis” end up close together because they’re both databases. When someone asks about “React experience”, the question gets converted to numbers too, and it naturally lands close to anything frontend-related in my content.

To convert text into these numbers, you need an embedding model. My first attempt used the HuggingFace Inference API, which worked, but had a problem: 0.5 seconds when the model was warm, 9.4 seconds when it was cold. HuggingFace spins down free-tier models after inactivity, so the chatbot would randomly hang for nearly 10 seconds. I switched to running the same model locally. all-MiniLM-L6-v2 is a popular open-source option, only 22MB, and it produces 384 numbers per piece of text in about 12ms:

import { pipeline } from "@huggingface/transformers";

const extractor = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");

async function embedText(text: string): Promise<number[]> {
  const result = await extractor(text, { pooling: "mean", normalize: true });
  return result.tolist()[0]; // 384 numbers
}

At build time, I run this on every chunk and save the results to a JSON file. At runtime, I embed the user’s question and find the closest chunks by comparing their numbers using cosine similarity (how much two sets of numbers point in the same direction):

async function searchChunks(query: string, topK = 8) {
  const queryEmbedding = await embedText(query);

  return chunks
    .map((chunk) => ({
      ...chunk,
      score: cosineSimilarity(queryEmbedding, chunk.embedding),
    }))
    .sort((a, b) => b.score - a.score)
    .slice(0, topK);
}

If you’re working with thousands of chunks, you’d want a vector database like Pinecone or Weaviate to handle the search. For a personal site with around 160 chunks, looping through all of them in memory works fine.

Generating the Answer

At this point I have the top 8 chunks most relevant to the user’s question. The last step is sending them to a language model to write a readable answer.

I went with Groq’s free tier running Llama 3.1 8B. The model doesn’t know anything about me by default. It only sees whatever chunks I send it. The system prompt tells it how to interpret the content and what the type prefixes mean:

const SYSTEM_PROMPT = `You are a helpful assistant on a personal website.
Answer questions using only the provided context.

Pay attention to type labels:
- [PROJECT]: Portfolio projects
- [WORK EXPERIENCE]: Employment history
- [BLOG POST]: Articles written
- [PROFILE]: Personal info

Keep answers concise and friendly. Do not make up information.`;

The API call:

const response = await fetch("https://api.groq.com/openai/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.GROQ_API_KEY}`,
  },
  body: JSON.stringify({
    model: "llama-3.1-8b-instant",
    messages: [
      { role: "system", content: SYSTEM_PROMPT },
      ...conversationHistory,
      { role: "user", content: `Context:n${relevantChunks}nnQuestion: ${question}` },
    ],
    temperature: 0.3,
  }),
});

Temperature controls how creative the model gets. At 0.3 (out of 1.0), it stays close to the most likely answer, which is what you want when accuracy matters. Conversation history (the last 10 messages) goes in with each request so follow-up questions like “tell me more about that project” work without losing context.

Deploying to Vercel

At this point everything worked locally and I was ready to deploy and move on. The chatbot ran as a serverless function through Astro’s Vercel adapter, the model was only 22MB, and the embeddings were a static JSON file. Should have been the easy part.

I deployed and immediately hit Vercel’s 250MB size limit on serverless functions. The model is only 22MB, so that wasn’t the issue. @huggingface/transformers depends on onnxruntime-node, which ships native binaries for every platform. They all get bundled into your function, and that alone pushes you way past 250MB.

There’s a lighter alternative called onnxruntime-web that uses WebAssembly instead of native binaries, around 11MB. But it’s built for browsers. Run it in Node.js and it tries to fetch WASM files from a CDN over HTTPS, which Node.js refuses to do.

The workaround: swap onnxruntime-node for onnxruntime-web with a pnpm override, copy the WASM files to a local directory during the build, and tell the runtime to load them from the filesystem instead of the CDN:

const wasmDir = join(process.cwd(), ".wasm");
onnxEnv.wasm.wasmPaths = {
  wasm: `file://${wasmDir}/ort-wasm-simd-threaded.wasm`,
  mjs: `file://${wasmDir}/ort-wasm-simd-threaded.mjs`,
};
onnxEnv.wasm.numThreads = 1;

With Vercel’s includeFiles bundling the model and WASM into the function, the same local inference that works on my laptop works in production. No embedding API, no cold starts, no cost.

What It Costs

  • Embedding a query: ~50ms
  • Searching 164 chunks: under 1ms
  • LLM response: ~400ms
  • Total: under 500ms

Monthly cost: $0. Groq’s free tier covers the LLM, embeddings run inside the serverless function, and chunk data is a static JSON file built at deploy time.

The whole thing is around 250 lines of TypeScript. There’s a chat button on my site if you want to try it.

Originally published on akrom.dev. For quick dev tips, join @akromdotdev on Telegram.