TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
A to-do list you scrub: a 3D dish sink in the browser
ProductivityJavaScript · CSSGitHub @brandonconcepcionOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A to-do list you wash. Every task is a dish in the sink.
S) to drop it into the soaking tub (6 spots; add or remove entries in SPOTS in main.js to change it): it sits in soapy water with bubbles rising, and the bottom panel shows how long it's been soaking. Click it later to finish it (✓ Done) or take it out (back to the sink). The longer it soaks, the more its grime loosens (fully after ~4 hours), and soaked dishes wash faster.Esc to put it back. Props always return to their spot and aren't saved. Clicking a letter of the Sinkful sign makes it hop.sfx.js, no audio files.E), or the pencil next to it in the list: change its title, notes, size or urgency. A new size or urgency rebuilds the 3D dish to match. Undoable.localStorage.python3 server.py # http://localhost:5173
server.py (no dependencies) serves the site and saves your data:
data/sinkful.json: the full current state (sink, tub, rack, every completed task with timestamps). Rewritten automatically on every change and loaded on startup, so progress survives across browsers and sessions.
data/history.jsonl: append-only activity log, one JSON line per event (added, soaked, taken-out, washed, removed, put-away, undo).
data/backups/: a snapshot of sinkful.json before it's overwritten (at most one every 5 minutes, newest 100 kept). To roll back, stop the server and copy one over data/sinkful.json.
Nothing is ever overwritten wholesale: every task carries an updatedAt, deletions leave tombstones, and the browser's copy, the file, and any other open tabs are merged task by task (newest wins). A save from a tab that's behind gets rejected by the server, merged, and retried.
A small note appears top-left only when saving to the file isn't working (browser-only or server unreachable). Under any other static server (e.g. python3 -m http.server) the app still works but saves only in the browser.
Use a separate data folder with python3 server.py 5175 --data /tmp/sinkful-test.
Static site with no build step. Three.js loads from jsDelivr.
Reset data in the console: sinkful.reset().
Watch a wash on load: #wash or #wash-pot. Slow motion: sinkful.speed(0.25). Preview the finale: sinkful.finale(). Fast-forward soaking: sinkful.ageSoak(4). Pick up a prop: sinkful.hold('toaster'). Performance: sinkful.perf() (draw calls, frame and hit-test cost), sinkful.meshBreakdown().
Rendering is on demand: nothing redraws while the scene is still, ambient tub motion redraws at 30 fps without redoing the shadow map, and static scenery, tub suds and each dish's grime are merged or instanced to keep draw calls low (~185 per frame with a full sink).
Live at brandonconcepcion.com/sinkful, behind a password. Pages has no server, so:
vault.json holds a GitHub token and a Gist id, AES-GCM encrypted with a key derived from the password (PBKDF2, 600k rounds). vault.js shows the lock screen and decrypts it in the browser; "Remember this device" keeps the unlocked token in localStorage.sinkful.json in that secret Gist, with the same merge rules as server.py. Every save is a Gist revision, so the Gist's history is the backup.node tools/make-vault.mjs (or --gist <id> to keep the existing Gist), then commit vault.json. Use a fine-grained token with only Gists: Read and write.The lock keeps the site to yourself, but it isn't server-side auth: the code is public and vault.json can be guessed at offline, so use a long password. Revoking the token cuts off every device at once.
The localhost copy (server.py, data/) is separate from the hosted one.
tools/calendar-dishes.gs is a Google Apps Script that adds today's calendar events to the Gist as dishes, every hour from 7am to 5pm. Only timed events you accepted or created count. The dish size follows the meeting length, from a glass for 10 minutes to a pot for 3+ hours. Each event gets one dish per day, keyed on the event id + date, so reruns, washing or deleting it never bring it back. Unwashed calendar dishes pile up like any other. Setup steps are at the top of the file.
The wall calendar next to the sign shows today's date. Clicking it syncs right away: it opens the script's web app in a small popup, waits for the sync to be recorded in the Gist's calendar.json, pulls in the new dishes, and closes the popup. The first click asks for the web app URL and saves it to the Gist for every device. On localhost the calendar is decoration only.
3D models: Kenney Food Kit and Furniture Kit (both CC0), see models/KENNEY-LICENSE.txt and models/decor/KENNEY-FURNITURE-LICENSE.txt.