Pages can save data now: localStorage, per origin.
Origin is scheme, host, and port, so moon:// and star:// don’t share a store. getItem/setItem/removeItem/clear/key, indexing (localStorage[k] = v), .length. Capped at 4096 keys, 5MB per origin.
Sources in DevTools has a Storage group, with Delete and Clear buttons.
Sources panel.
It lists everything the page fetched, grouped into: Document, Scripts, Stylesheets, Images, Media, Fonts. Text files have a viewer, wrap toggle, find-in-file, and line numbers. Images show the texture. Media points to the page’s own player rather than opening a second stream, since a URL only has one range cache.
Network tab and DevTools got restyled to match the rest.
Every request now gets timed in stages: resolve, connect, TLS, send, wait, download, each stamped as it happens inside perform_request. The panel draws that as a bar per stage, plus headers, response preview, and a security tab with the TLS info for that request.
Then the devtools were redesigned to match the browser style from the last devlog.
Ask is done.
You type a question in plain English, and a model writes the database query for it. You get a table back, and bars if it makes sense to chart.
It only gets to write queries out of a list I picked. No writes, its own read-only database user, a time limit, and a rate limit on how many questions you can ask an hour. If it writes something outside that, it just gets refused.
Also made API docs with examples for every endpoint, a README, and a license. There’s a docs button in the sidebar now.
Devlogs page. Every devlog on the site in one feed.
Cards with the author, the project, the numbers, the text, and the images or videos. Filter by how recent, sort by newest, likes, comments, or views, or search.
Search reads the body. Type “cat” and you get everyone’s cat posts, or put quotes around something to match it exactly. Whatever you searched gets highlighted in the card.
Also put the little star creatures around the pages. (the sillies)
Projects page is done.
Same podium as People but with the banners behind the top three. The list under it includes a thumbnail, the owner, and hours and devlogs in a single row. Buttons to filter to Super Stars or hardware.
Click one, and you get everything about a project. Banner, tiles, a chart from 1 to 30 days, every ship, and all the devlogs. Sort them by newest, longest, most liked, most discussed, or most viewed.
People page: Leaderboard with a podium for the top three; click through to anyone’s profile.
Search goes through the API since whoever you’re looking for usually isn’t on the page you’re on. It tries handles that start with what you typed first, then ones that just contain it.
Added a blacklist. Some people don’t want to be tracked, so their ID goes in, and everything of theirs gets deleted.
Fixed missions. Some pay a fixed amount instead; others go to rating; and none of that shows up as a payout on the page. Now each ship gets credited properly.
Also added those silly creatures to the global page.
Comments. A thread gets queued when its count moves, and they run in their own lane so they don’t starve behind everything else. Deleted ones get marked gone instead of vanishing.
I got the shop too. Prices are per region, and the region is just a cookie, so it just grabs the catalogue seven times. That gives you the spread: how much more the same thing costs depending on where you are.
Devlogs have their own history now, so you can watch one post’s likes over time.
Fixed a demotion bug. Crawling a page more often used to make it go cold faster, which is backwards. Now it just checks how long it’s actually been quiet.
Frontend started too. One page with the totals and a chart. The rest of the tabs are greyed out. And that’s what I’m going to be working on right now.
History endpoints.
Projects, users, and the whole platform all run through the same thing. Pick your metrics, pick hourly, daily or weekly, get points back.
Points only get written when a number moves, which means quiet stretches look like gaps. There’s a fill option that carries the last value forward so a flat line renders as a flat line. Ask for too wide a range, and it says no instead of trying.
Every response now says when the data was last seen and whether that’s old. Responses also carry an ETag, so asking again for something that hasn’t changed costs nothing.
Also added a platform endpoint for the site-wide totals, and a single devlog one.
It can be something like, “What’s the best rated project on SD?” or “How many users have over 20 followers?”
There’s a queue of everything worth crawling, and each page gets a tier. Pages that keep changing get checked often; pages that never change drop to the slow lane.
It reads the sitemap to find pages and see what changed recently. But the sitemap misses a lot, so it also just walks id ranges past the highest one it knows is real. Most of those 404s, that’s fine. Owners that never got listed get found through their projects instead. Hidden and unverified profiles still parse.
Users and leaderboards.
It reads profiles now, by handle or ID. Followers, streaks, achievements, what projects you’re on. Renames are fine, the old handle still finds you.
Project pages only show handles, no IDs, so those rows just wait until the user gets crawled and then link up. Order doesn’t matter.
Ships only pay after review, so shipped hours and paid hours aren’t the same. Payouts also use capped hours that aren’t shown anywhere, so I work those out from the payout and multiplier. Unpaid hours get an estimate.
Super Star badges too.
New project! Stardance Stats, a better way to browse Stardance.
The crawler is polite. It’s rate-limited, retries when things fail, and backs off entirely if the site starts struggling. Pages that haven’t changed don’t get re-parsed.
Parsing hooks onto stable attributes instead of layout, so a restyle won’t break it. It reads devlogs and ships off the feed, then checks the numbers against the page’s own totals. If those drift too far, something broke.
Everything goes into MongoDB as time series. Counters like devlogs or stardust can’t go down, so if they do, it’s a bad parse, and it gets rejected. First crawl also backfills old history from dates already on the page.
oh btw all testing is done on @The_Craw project, so shoutout to him.
Restyled the browser to match Nebula, and gave it real icons.
The toolbar and tab icons used to be hand-drawn with ImGui lines. Now they’re Lucide SVGs, rasterized once and tinted.
Layout used to be random numbers everywhere; now it’s all one Trim namespace. Colors follow one ramp: dim, then mid, then bright when focused.
DevTools! F12 or Cmd/Ctrl+Shift+I opens a dock on the right side of the page.
Elements works: a picker button lets you click something on the page to select it, or walk the DOM tree directly. Selecting a node shows its computed styles and box model, with real layout padding rather than just the CSS value.
Console works too. Whatever you type evaluates in that tab’s own Lua engine, with history on up/down.
Network, Sources, and Metrics are just tabs for now, not built yet.
Im going to be working on design now 
Added live search and SVG rendering.
Type in the sidebar or the home search, and a dropdown fills in as you go. That needed a new input event for page scripts, they only got click before. Queries are percent-encoded before they hit the URL, with a small urlenc() since this runtime has no encodeURIComponent.
Also added recent pages on the home screen, capped at 3, deduped, ordered so two clicks in the same instant land right.
Analytics got a range picker, and the drawing underneath it got a lot cheaper to run.
The panel graphs now go from 1h up to 1y, not just a fixed 14 days. That needed a second counter: stats_min, one document per domain per minute, which only exists to answer the sub-day windows and ages out on a TTL index. The old daily stats collection stays forever so the all-time total is still exact. Past a month the buckets widen to weeks then 30-day chunks, since 180 daily points would just read as a smear at chart width.
The nebula background got refactored for speed: prep() runs once per canvas resize and does all the expensive math, square roots, atan2, walking every line segment, into a flat array; lit() runs per frame and just reads that array plus whatever actually changes frame to frame (the shine position, the cog’s rotation). Same picture, way less work per frame. Hex color parsing got a fast path too, since a canvas setting fillStyle per dot was hitting the full color parser thousands of times a frame for the one form it ever uses.
Canvas itself grew a few things: fillRect/strokeRect take an optional radius now, measureText reads the actual font the page pushed instead of the UI default, and hoverX/hoverY are readable off a canvas element. The ctx method lookup also switched from a chain of strcmp to a real table, since a heavy canvas loop calls into it constantly.
requestAnimationFrame self-paces now. A callback that runs long gets a gap after it sized to what it just cost, capped at 500ms, so one expensive canvas can’t eat the whole frame budget. It also only fires for the visible tab and stops while the window’s minimized; timers and fetches keep going regardless, same as a background tab in a real browser.
Added analytics and worked on the domains page.
The resolver bumps a counter every time it answers a lookup: one document per domain per day, not per query. It’s best-effort: if the analytics write fails, the answer still goes out. Missing days are zero-filled, so a new domain shows a flat line instead of a gap.
That feeds two chart types in Lua, chartBars and chartLine, just canvas: fillRect bars or a stroked polyline. Zero draws nothing, and only a real value gets a 2px floor. The home page gets one combined trend line across every domain, the domains list gets a sparkline per row.
Also fixed a flex-grow input because it didn’t fill its row width. Inputs and textareas now stretch to match, so form rows line up.