A Windows Desktop Where Icons Clock Out, Fight, and Play Soccer on Their Own
theme: channing-cyan
Doubao Seed Updated Again: A Model Card That's Always "Latest"
This time, Doubao isn't launching a fixed version that will become outdated after a while, but Doubao-Seed-Evolving: a latest branch specifically for Coding and Agent scenarios. The core of Evolving is continuous weekly updates. Developers don't need to chase a new model every so often; by using the same model endpoint, the underlying capabilities will continue to strengthen with each weekly iteration. You can think of it as a model card that always stays "latest."
The two most intuitive points of this upgrade are the 1M context window and stronger long-horizon task capabilities. The 1M context allows the model to handle larger code repositories, longer requirements, and more historical information at once; the long-horizon capability makes it less likely to lose track of the goal during multi-turn planning, tool calls, code modifications, and test verification. For a Coding Agent like Codex, being able to continuously execute a complex task is more important than just writing a single block of code well.
Alright, today we'll configure this model with Codex and test its actual performance.
I won't repeat the commands for accessing and configuring the model here. Volcano Engine has already prepared configuration steps for Codex CLI, Codex Desktop Client, etc. Those who need them can follow the instructions at https://console.volcengine.com/ark/region:cn-beijing/docs/82379/2556054?lang=zh.
The red box in the image marks the chapter directory of the official tutorial. Just select the corresponding section based on the client you are using. This article will not repeat the configuration process.
Desktop Brainstorm
Every day after booting up, software icons like Chrome, Edge, WeChat, and VS Code sit quietly on the Windows desktop. Their only job is to wait for me to double-click.
I suddenly had a slightly silly but increasingly vivid thought: If I get off work, do my desktop icons get off work too? Would they come out and move around on their own? Browsers might not see eye to eye, chat software might huddle together whispering, and development tools might form a team to play soccer.
Later, this brainstorm really became a small Windows tool—"Desktop After Hours." Some icons sprout limbs and stroll around the desk, some hide in a corner to chat, and two icons might suddenly start fighting. After a while, the whole desktop of software will automatically split into two teams to play soccer. The whole process requires no operation from me; at most, I click "Mediate" when they are fighting.
Below is the actual running footage: the left side shows the real desktop shortcuts, untouched; those on the right, with limbs and running around, are the character avatars drawn by the program.
For this idea, I really only said one sentence at the beginning. The model first judged it was feasible, then provided the key solution of "don't move the native icons, draw character avatars instead." Below is an excerpt of the original conversation at that time:
The red labels mark my initial idea and the key solution given by the model: leave the native desktop icons untouched, and generate character avatars separately.
This Idea Almost Got Overcomplicated Again
"Fighting and playing soccer" sounds a lot like a game. The first round of proposals naturally included gameplay like clicking characters, dragging characters, specifying chat partners, and actively initiating soccer matches.
But I felt it was wrong after reading it. What I wanted to see was software icons moving around on their own when I wasn't operating the computer, not adding another little game that required operation and had winners and losers.
So I set the boundaries firmly:
The red labels mark the product boundaries I redefined and the model's adjustment results based on this requirement.
This sentence almost determined the entire product later:
- Cannot click characters, cannot drag characters;
- Cannot specify who chats or who fights;
- Cannot manually start a match, nor control any team;
- No points, achievements, or win/loss;
- The desktop stage is always mouse-transparent;
- The only actions are swapping icons in/out, and mediating during a fight.
If not mediated, the conflict will end on its own after a few seconds. Mediation just leaves a very light entry point, not a mandatory gameplay mechanic.
The project only started to go smoothly from here. It's not "a mini-game overlaid on the desktop," but more like a desktop fish tank that changes on its own.
Don't Write Code Yet, First Cut Down Three Approaches
There are three ways to make icons move. I laid out the risks first, without rushing to start the project.
| Approach | How it looks | Actual problems | Conclusion |
|---|---|---|---|
| Directly move Windows native icons | Most realistic, icons truly leave their original positions | Will disrupt the user's desktop layout, and carries system behaviors like double-click, drag, right-click | Won't do |
| Make a regular desktop pet with a built-in set of software icons | Safe, animations are easy to do | Not the icons on the user's computer, missing the most crucial sense of immersion | Won't do |
Read .lnk icons, then draw a copy as character avatars |
Real icons don't move, yet can use local machine icons | Need to handle transparent windows, click-through, and desktop layering | Choose this one |
The first one had the most gimmick appeal, and I thought of it first too. But if coordinates, DPI, or Explorer state ever had a single issue, the user's arranged desktop would be messed up. Not to mention a Chrome icon that's playing soccer suddenly triggering a double-click and opening the browser.
So the first rule of the project wasn't "animations must look good," but:
Do not move, delete, rename, or execute any real shortcut.
The program only scans the top-level .lnk files on the current user's desktop and the public desktop. After getting the name and icon, it draws a copy in a transparent window. You can already see in the screenshot's red box: the real shortcuts stay on the left, and the characters with limbs move in another layer. Exit the tool, this layer disappears, and the desktop remains untouched.
The First Kick Hit a Windows Wall
The transparent stage must not block the mouse
Making an image move in WPF isn't hard; what's hard is making an entire window seem non-existent.
The simplest transparent window, though its background is invisible, still eats mouse events. The icons are running on the desktop, but the user can't click the real desktop. Setting the window to always-on-top is even worse; a bunch of characters would float over browsers and IDEs.
In the end, I paused event development and only made a transparent stage prototype. The window needed to simultaneously satisfy three things: not receive clicks, not steal focus, and not appear in Alt + Tab. The core is these few Win32 styles:
var current = GetWindowLongPtr(handle, GwlExStyle).ToInt64();
SetWindowLongPtr(
handle,
GwlExStyle,
new IntPtr(current | WsExTransparent | WsExToolWindow | WsExNoActivate));
The mouse will pass through the stage and continue to land on the Windows desktop; the characters won't steal keyboard focus; and no extra transparent window will appear when switching windows.
There's another rule: characters are only shown when the desktop is in the foreground. Open a browser, IDE, or other regular software, the stage hides, and event timers pause; return to the desktop, and they continue their activities. This way, it feels like part of the desktop, not a floating ad always on top of all windows.
One "Hide Panel" Click, and All Icons Disappeared
After the prototype passed, I thought the most troublesome part was over. But as soon as the first version ran, a very straightforward problem appeared: clicking "Start Show" hid the control panel, and the desktop characters disappeared along with it.
No need to check the logs to know this was wrong.
Upon investigation, it wasn't that WPF closed the stage, but that the logic for "is the desktop in the foreground" was too rigid. I also had SpeedBall running on my computer, which is a desktop companion program. The moment the control panel hid, SpeedBall briefly became the foreground window, and the program mistook it for a regular window like a browser and paused the entire stage.
After the fix, foreground windows are divided into three categories:
- Explorer desktop: Show;
- This tool and recognized desktop companion programs: Show;
- Regular apps like browsers, IDEs: Hide and pause.
This problem was very small, but it illustrates the actual development process better than "how many lines of code the model wrote at once." The first version given by Doubao-Seed-Evolving also missed scenarios; you still have to actually run the program and continue fixing based on the errors in front of you.
38 Icons On Stage at Once, I Couldn't Stand It Myself
After the stage stopped disappearing, I ran into another issue that wasn't a bug, but a terrible experience: the program put almost all 38 valid shortcuts it scanned onto the stage.
The screenshot was indeed lively, but in reality, it looked like desktop rush hour. Character names overlapped each other, you couldn't tell who was chatting with whom, and finding a specific icon was very laborious.
My modification request was very direct. Along with the "icons disappeared after hiding the panel" problem, I fed both back to the model in a single message:
The red labels mark the actual runtime feedback and the result after the model completed the modification, testing, and repackaging.
So the initial lineup was changed to the first 10 valid shortcuts. The remaining selectable icons are kept on the left, and the user decides who to swap in. Existing users from older versions couldn't be treated uniformly either: only when the old save file was close to "all icons on stage" would it automatically shrink to 10 upon upgrade; if the lineup was originally hand-picked by the user, it would be kept as is.
public const int DefaultInitialCount = 10;
return valid
.Take(Math.Min(limit, DefaultInitialCount))
.ToArray();
To supplement the control panel screenshots for this article, I also caught a missed issue: the feature had long been changed to default to 10, but the panel title still read "Show All by Default." The code was new, but the text was stuck in the old version. I fixed this too, repackaged, and ran the 25 tests again.
This image is clearer than just writing "supports custom lineup": the right side shows the first 10 on stage, the left side shows the remaining icons, and in the middle are only swap-in, swap-out, and character attributes. At the top, besides Start, Pause, and Hide Panel, the only button related to events is "Mediate."
Moving Doesn't Mean There's Something to Watch
How automatic events are chosen
Initially, the characters just moved randomly with their icons on top. Technically, they were "moving," but after watching for two minutes, it got boring. No one had any relationship with anyone else; chatting looked like two images accidentally bumping into each other, and during a fight, a circle of passersby crowded around.
Later, states, stamina, personality, relationships, and event cooldowns were gradually added. Normally, characters stroll and rest; when the scheduler's timer is up, it finds people from idle characters to chat or conflict. The same pair won't chat again immediately after just chatting; there's a cooldown after a fight; only one multi-person event can happen at a time.
Automatic events aren't randomly picked every frame. The scheduler first checks if the feature is enabled, then checks the cooldowns for fighting, soccer, and group activities. A single random roll will sequentially fall through to chat, fight, soccer, group dance, or parade:
var roll = _random.Next(0, 100);
if (roll < 42 && _options.EnableChat && TryStartChat(ignoreRelationship: false))
{
return true;
}
if (roll < 58 && _options.EnableFight && _fightCooldownSeconds <= 0 && TryStartFight(ignoreRelationship: false))
{
return true;
}
if (roll < 74 && _options.EnableFootball && _footballCooldownSeconds <= 0 && TryStartFootball())
{
return true;
}
if (_options.EnableGroupActivities && _groupActivityCooldownSeconds <= 0)
{
return roll < 87 ? TryStartDance() : TryStartParade();
}
This preserves a sense of randomness while preventing a fight from happening again right after one, or soccer and a group dance from occurring simultaneously.
Rules alone aren't enough; you need to let people understand what's happening at a glance:
- Chatting partners move closer, a relationship line appears between them, and other characters automatically clear a space;
- A fight keeps at most 6 spectators, and they can only stand on the outer circle;
- States like "Walking, Chatting, Fighting, Ball" are written directly above the character, no need to guess from Emojis;
- The icon itself serves as the body, with four bendable limbs added, using different actions for different events.
Below is a conflict captured during an automatic demo. The program chooses who fights on its own, with at most 6 spectator characters nearby; other icons won't all crowd into the conflict center.
I didn't draw a set of exclusive characters for each piece of software. WeChat is still the WeChat icon, Edge is still the Edge icon. The limbs just make them look more like they're moving, without making the original software unrecognizable.
Soccer Was Also Revised; Initially, Not Everyone Participated
Everyone participates, odd numbers aren't left out
The requirements document initially stated to automatically select 4, 6, or 8 characters to play soccer, with equal numbers on each team—very standard logic. But when it actually ran, it felt a bit strange: a few people were playing soccer on the field, while the rest stood around idly, even wanting to continue their stroll.
I later changed the rule to "the entire current lineup participates." Even numbers are split equally; odd numbers don't leave anyone on the sidelines, with the two teams differing by at most one person. Once soccer starts, all characters are locked into the match state and cannot simultaneously chat, fight, or be swapped.
The code first confirms all current characters can participate, then directly calculates the two team sizes based on the total number. For odd numbers, the left team will only have 1 more than the right team, never leaving the last character out:
var selected = _characters
.Where(item => item.State != CharacterStateKind.Leaving)
.ToArray();
if (selected.Length < 4 || selected.Length != _characters.Count)
{
return false;
}
Shuffle(selected);
selected = selected.OrderByDescending(FootballStrength).ToArray();
var leftTeamSize = (selected.Length + 1) / 2;
var rightTeamSize = selected.Length / 2;
The 38 characters in the image were all manually added by me for a stress test, not the default scene. The top score, team colors, countdown, and the "Ball" text above characters are all automatically controlled by the program. People just watch, not responsible for kickoff, movement, or shooting.
After soccer was running smoothly, I added a group dance and a desktop parade. The rules didn't start from scratch; the entire lineup still enters the group event together and uniformly returns to normal state afterward.
With a large number of people, continuously creating brushes, transforms, and line objects at 60 FPS is too wasteful. In the end, rendering was lowered to 24 FPS, and these visual objects were reused. Desktop animations don't need game-level frame rates; 24 FPS looks sufficient, and it completely pauses when a regular app is brought to the foreground.
What Doubao-Seed-Evolving Actually Did in This Project
I didn't just give it one prompt and wait for a complete piece of software to fall from the sky. The actual sequence was more like this:
- I first stated the brainstorm, and it helped break down the product form and risks;
- I rejected the gamified interactions and demanded a change to a pure viewing tool;
- First write the requirements plan and development plan, then put the two documents together to find errors;
- Make a minimal prototype for the transparent stage first, confirming click-through and window switching could work;
- Then implement desktop scanning, character states, and automatic events;
- Each time, run it on real Windows, take screenshots, and continue fixing upon discovering problems;
- Finally, supplement tests, documentation, a self-contained release package, and SHA-256.
During the requirements phase, I also didn't let the model directly start writing code, but first required it to produce a requirements plan and a development plan, then put the two documents together for a re-read to find errors. The red labels in the image mark my requirements and the model's delivery.
The final tech stack is only C#, .NET 10, WPF, Win32/Shell COM, JSON, and xUnit. No Unity, no Electron, and no model SDK.
The project was also only split into two main parts: Core manages characters, relationships, cooldowns, and event rules; App manages WPF, desktop scanning, the transparent stage, the system tray, and local save files. This way, whether soccer involves everyone or whether mediation causes duplicate settlements can be directly tested with unit tests, without having to stare at the desktop waiting for events to happen each time.
The first version of the implementation from Doubao-Seed-Evolving also didn't automatically cover all real-world scenarios. Icons disappearing after hiding the panel, the default full lineup, and old text not being updated—these aren't things you can discover just because "the code compiles." They can only be caught through actual running and review, and then the results are handed back to the model for modification.
What This Version Can Do Now
The current version can already go through a complete loop:
- Scan top-level
.lnkfiles on the current user's and public desktops; - Default to 10 characters initially, then allow the user to swap them in and out;
- Characters automatically stroll, rest, chat, and engage in cartoonish conflicts;
- Everyone automatically plays soccer, does a group dance, and parades around the desktop;
- Fights can be mediated from the control panel or system tray, or ignored;
- The stage continues running after hiding the control panel;
- The stage automatically hides when regular software is opened, and resumes when returning to the desktop;
Ctrl + Alt + Escallows for an emergency exit.
All settings, lineups, relationships, and recent desktop chronicles are only saved in %LocalAppData%\DesktopAfterHours. The program does not read chat logs, browsing history, or document content, nor does it send shortcut information out.
Finally
Looking back after finishing, the most interesting part of this project isn't simply how much more code AI wrote, but how a very vague brainstorm was gradually reined in to become something runnable: not touching real icons, not requiring the user to play, defaulting to only 10 characters, and being able to continue modifying based on on-site results when problems arose.
From this experience, I think Doubao-Seed-Evolving is very suitable for this kind of project where "you start with a single idea, then build and modify as you go." It can first break down a brainstorm into requirements and risks, then continue to write plans, set up the project, modify code across files, supplement tests, and package. The product boundaries defined earlier can also be carried into the later implementation, without needing to re-explain everything from scratch each round.
It's not right the first time, every time. Problems like hiding the panel, the default lineup, and old text were only discovered after I actually ran the program. But the model can continue to investigate causes, modify code, and run verification based on this specific feedback, which is more useful than generating a seemingly complete demo in one go.
Now when I return to the desktop, I occasionally see browsers and development tools lined up in two teams playing soccer, or two icons hiding in a corner chatting. I usually watch for a few seconds, then continue with my own work. For this little tool, these few seconds are enough.