Tamaru banner

Tamaru

10 devlogs
11h 2m 7s

A trackball that scrolls the page, made for fun.
Uses a bunch of cool CSS to appear 3D, and has some cool physics!

This project uses AI

Used AI to improve my readme a bit, but not docs
Edit: To clarify, the ONLY use of AI was for the README, nothing else.

Demo Repository

Loading README...

NellowTCS

Shipped this project!

Hours: 2.11
Cookies: 🍪 66
Multiplier: 26.13 cookies/hr

I built Tamaru, a physics-driven virtual trackball widget for the web, and this ship is v0.1.1 with the Chrome extension!

Download the Chrome extension on the release page here!

The extension lets Tamaru haunt any website you visit, with a full interesant settings popup that themes itself to match your orb. It autosaves everything, has a floating orb header, themed section icons, sliders with live value displays, the works.

This ship also includes a handful of fixes and additions to the core library: audio now properly resumes on first interaction (browsers are rude about AudioContext and suspend it until a user gesture, so I added resumeIfNeeded() on every pointer event), a new startMinimized config option so the orb can boot as a tiny lil widget instead of slamming its full UI into your face, and the extension’s dependency situation got untangled so it actually builds from source properly.

The hardest part was honestly wiring the extension together cleanly. Getting the Vite alias pointing at the right build output, Chrome storage syncing correctly, the popup theming matching the orb, and audio not being silent on first load all had their own little moments. But it works now and it’s so cool seeing Tamaru just… exist on every page.

NellowTCS

I PUBLISHED V0.1.1!!!
IT’S ON NPM.
IT’S REAL.
THE EXTENSION EXISTS NOW.
As a .crx file 😔 but still

So uh.
I was supposed to just clean things up a bit.

Anyway.

THE EXTENSION IS NOW A REAL PROJECT.
And also the orb can make sounds again.
And also it can start already minimized.
And also dependencies are no longer lying about what version of Tamaru they use.
The math checks out. totally.

What I Did

Fixed audio!!
Browsers are incredibly rude about AudioContext. They suspend it until a user gesture happens, which means if you load the extension and haven’t touched anything, the orb just silently judges you.
Fixed by adding resumeIfNeeded() that gets called on every pointerdown, on the trackball, on the widget handle, AND on the stick mode button.
The orb now makes sounds again. as it should. it was sad without them.

Added startMinimized!!!
New config option: startMinimized: boolean (defaults to false)
If you set it to true, Tamaru boots up as a tiny lil orb instead of the full widget.
Super useful for the extension so it doesn’t slam its whole UI into your face the second you open a tab.
Added it to the demo config panel, the docs, and the types. it exists everywhere now.
The extension’s popup even has a “Start Minimized” toggle defaulting to checked, because yes, unobtrusive is the vibe.

Fixed the extension’s dependency situation (twice lol)
First I removed the broken "tamaru": "0.1.0" from dependencies because that was the old version and it was Lying
Then I wired up a Vite alias so the extension points directly at ../Build/dist/main.mjs
So it builds from source, properly, like a grown-up project
Then I bumped the npm package to v0.1.1 and updated the lock file to match
Also added @types/node, prettier, typecheck and make-pretty scripts to the extension because Past Me left it looking a little eeeh in there

Bugs Fixed

  • audio not playing on first interaction (fixed via resumeIfNeeded on every pointer event)
  • extension depending on the wrong version of Tamaru (fixed, twice, don’t ask)
  • orb spawning full-size on every page and startling people (fixable now via startMinimized)

Changelog

Attachment
0
NellowTCS

I was busy…

Anyway.
THE POPUP HAS BEEN REVAMPED.
THE ORB CONTROL PANEL IS NOW A WHOLE APP.


What I Did

  • Added icons!!!
    (this was supposed to be the only thing I did…)
  • And then I revamped the entire UI
  • Rewrote the popup from scratch
    it now looks like a tiny cyberpunk settings dashboard
  • Added theme classes so the popup changes theme with the orb
    (yes the popup is now fashionable)
  • Added autosave because clicking “Save” is for mortals
    sliders? autosave
    checkboxes? autosave
    dropdowns? autosave
    breathing near the UI? autosave
  • Added a background script for absolutely no reason (/j it’s for the future)
    it just logs “installed”
  • Cleaned up the content script
    now it actually listens for config changes like a polite little gremlin
  • Deleted like 500 lines
    Added like 600 more
    The math checks out. totally

Popup UI Now Has

  • floating orb header
  • themed section icons
  • cards
  • sliders with live value displays
  • custom selects
  • toggles
  • scrollable container
  • footer button

ah yes consistency

The extension stole (with permission) Tamaru’s themes.

theme-aqua
theme-red
theme-glossy
theme-metal
theme-neon
theme-sunset

The popup now cosplays as your orb :D


Autosave Because I’m Extra

  • sliders -> autosave
  • selects -> autosave
  • checkboxes -> autosave
  • text inputs /j -> autosave

Replaced with ✨ better ✨ things.

better build

  • added base: "./"
  • added @types/chrome
  • reorganized everything into src/ and styles/
  • extension now looks like a Real Project™ now
Attachment
Attachment
Attachment
Attachment
0
NellowTCS

SO I STARTED AN EXTENSION THINGY
VERY BROKEN
BUT STILL

Anyway.
THE ORB HAS ESCAPED THE DEMO.
IT CAN HAUNT ANY WEBSITE NOW.

What I Did

  • Made a whole Chrome extension folder because
  • Added a content script that just… summons Tamaru on every page.
  • Wrote a popup UI that is WAY too powerful for a first version yet even more broken
    Like. It has sliders. Dropdowns. Checkboxes. A whole form.
  • Added a settings panel with:
    • size
    • theme
    • sound
    • rollSoundLevel
    • haptics
    • scroll mode
    • fallback mode
    • friction
    • sensitivity
    • snap distance
    • stick mode
    • stick mode cycle key
    • stick mode cycle snap

uhh will make it better in upcoming devlogs

Changelog

Attachment
Attachment
Attachment
Attachment
0
NellowTCS

Shipped this project!

Hours: 8.92
Cookies: 🍪 318
Multiplier: 29.74 cookies/hr

I built Tamaru, a physics‑driven virtual trackball widget for the web!
It lets you scroll any page or container by flicking a little 3D orb, complete with inertia, audio, haptics, themes, and now… Stick Mode (pointer‑lock scrolling), which nearly broke me (/silly) but was so worth it.

The hardest part was definitely Stick Mode. Pointer lock, scroll‑target cycling, highlight logic, restoring state, and making it all feel like a real trackball was a whole boss fight. I figured it out by rewriting half the scroll engine, adding a target‑cycling system, and bribing the browser with sheer stubbornness (yes your browser’s now corrupt 😔).

I’m really proud of how polished it feels now, the physics, the audio, the theming, the demo, the docs, everything. It started as a tiny 6 AM experiment and somehow turned into a full npm package with real features and real users. I’m honestly so happy with how it turned out.

NellowTCS

So uh.

I DID IT.
I ACTUALLY SHIPPED TAMARU.
It’s on npm.
It’s real.
It’s alive.
It has provenance logs and everything.
I’m screaming.

(I’m using the bookmarklet, by the way, in the first two images)

Attachment
Attachment
Attachment
2

Comments

NellowTCS
NellowTCS about 2 months ago

*Last two images, the upload order is backwards

NellowTCS
NellowTCS about 2 months ago

HEY REVIEWERS/READERS

SO someone asked me to make this an extension. firstly WHY DIDN’T I THINK OF THAT, and secondly I’m working on that and will reship soon™

NellowTCS

so uh. stick mode.

I finally did it.
I added stick to cursor mode.
And let me tell you:

this was
SO
HARD.

like “why am I doing this to myself” hard
like “pointer lock is a demon” hard
like “scroll target cycling is a cursed ritual” hard

BUT ALSO
SO
WORTH IT.

the descent into stick‑mode madness

pointer lock hell

I had to fight the browser for custody of the mouse.
It did NOT want to give it to me.
I won.
with a smirk cause why not >:3

mini‑mode orb

stick mode now force‑minimizes the orb
centers it

absolute movement scrolling

no more dragging the orb
no more “go to the corner”
just pure raw mouse movement -> scroll velocity
like a REAL trackball
but digital!

target cycling

hold Shift
scroll wheel (or two fingers on laptop)
Tamaru goes:

“oh you want to scroll THAT div?
okay let me highlight it for you”

it even scrolls it into view configurably
because I’m extra.

state management purgatory

enter stick mode
exit stick mode
restore position
restore size
restore sanity (attempted, not guaranteed, often fails)

UI updates

added a cute little ⌖ button
it mocks me
but it works

do these exist? yes. was it pain? yes.

  • stick mode button (desktop only because mobile said “no”)
  • pointer‑lock based scrolling
  • scroll target cycling with Shift + wheel
  • highlighting the active scroll target
  • auto‑snap into mini mode when entering stick mode
  • auto‑restore when exiting
  • scrollIntoView because I’m dramatic
  • scroll engine now respects stick mode like it’s a law of nature

bugs I fixed (and bugs I created then fixed)

  • stick mode not restoring size
  • stick mode not restoring position
  • stick mode scrolling the wrong thing
  • stick mode scrolling nothing
  • stick mode scrolling EVERYTHING
  • highlight not clearing
  • highlight clearing too fast
  • highlight clearing too slow
  • scroll engine forgetting what a scrollable element is
  • me forgetting what a scrollable element is
  • me forgetting who I am

demo

updated the demo again
because of course I did
stick mode works
it’s beautiful
it’s terrifying
I love it

Attachment
Attachment
Attachment
0
NellowTCS

So uh…
I kinda want to ship this today…
Maybe a bit obsessed but here’s another devlog haha.

I decided to try not to do like 10 things in one devlog.
And then I…
did not do that.
Apparently I cannot touch this project without accidentally doing a full renovation.

Anyway.

Stuff I Did

  • fixed the GitHub Pages workflow (for real this time… I think… I hope…)
  • added a favicon because the docs looked naked without one
  • added an icon.png because why stop at one image when you can have two
  • updated the docs config so the favicon actually shows up instead of silently judging me
  • added OpenGraph images so sharing Tamaru links doesn’t look like a 404
  • added a “Try the Demo” link because yes, you should try the demo (duh)
  • updated the README to stop lying about “no external dependencies” (pain)
  • tweaked the TODO list because I keep pretending I’m done
  • moved “tweak base config” to Done because it actually is good now
  • stared at the workflow file like it personally betrayed me
  • committed something
  • realized I forgot something
  • committed again
  • realized I forgot something else
  • and realized i made the devlog look like an essay again

Bugs I Fixed

  • “I thought I changed this” (I did not)
  • “why is the favicon not showing up” (wrong path)
  • “why is the docs folder empty” (wrong folder)
  • “why is the README lying” (me)
  • “why is the demo favicon broken” (also me)
  • “why is this taking so long” (me again)

Demo

Updated the demo again and it now is curvomorphic and prettyyy :3
Now with a working favicon.
Probably.
Confirmed now, yeah it works
I love it, it’s perfect :D

Attachment
Attachment
0
NellowTCS

So uh.

I did… a lot of things.
Like, a silly amount of things for someone who allegedly “was just fixing bugs.”

This commit arc is basically:

“I’m only going to clean my room a little”
proceeds to renovate the entire house, add two new rooms, repaint the walls, and fix a installed sound system

Anyway.

Stuff I Did

  • rewrote some janky code because Past Me was clearly having a moment
  • fixed like 6 bugs that were all conspiring together
  • made mini‑mode actually behave
  • added visibility + blur handlers so the orb stops rolling when you tab away (polite orb!!)
  • fixed pointer‑events so the widget stops gatekeeping interaction (polite orb x2!!!)
  • made the rolling sound smoother, less “grate‑y”, more “roll‑y”
  • tuned the filters again because apparently I’m an audio engineer now (if that was ever a question)
  • fixed the tiny audio gap when continuously scrolling (this one was haunting me, I don’t like not seamless sound)
  • improved haptics with iOS detection + rate limiting so it doesn’t vibrate itself into another dimension
  • added scrollbar‑hiding CSS because WebKit refuses to listen to me (seriously Apple?!! why must you be so different)
  • updated the theme loader
  • updated the README
  • updated the demo
  • updated the TODO (again…)
  • oh and uhhh

I added two new themes

neon

gradient, heheheheheheh
(I NEED MORE GIVE ME MOREEEEEEEEEE)

sunset

Orange, mic drop.
(but also it looks like mars sob)

Features

  • smoother rolling sound with smarter fade logic
  • mini‑mode that doesn’t break when you click it (yay)
  • scrollbars that disappear like they were never there
  • haptics that actually work and don’t fire 900 times a second
  • glossy theme is now ✨ glossier ✨
  • rolling physics + audio feel way more natural now
  • the orb is just… nicer?? like it has manners now almost??

Bugs I Fixed

  • rollSoundLevel mysteriously breaking (fixed)
  • rolling continuing after tabbing away (fixed)
  • clicking the mini trackball breaking the ball (fixed)
  • stuff near the trackball not being interactive (fixed)
  • WebKit scrollbars ignoring me (fixed via CSS war crimes)
  • tiny audio gap during continuous scroll (fixed)
  • haptics not working (fixed)
  • glossy theme not glossy enough (fixed)
  • rolling too grate‑y (fixed)

Demo

Updated the demo again because if I’m suffering, you get to see the results :3

Attachment
Attachment
Attachment
0
NellowTCS

So… I did a bunch of one thing:

This commit message best summarizes it:

I just wrote a manual audio engine
d6e51b1
 ¡ 
2 hours ago
this took ages

help
help me
someone
/j this is normal for me I made an *audio codec*
no it isn't why didn't i use audio files
WAIT
OH
OMG
AAAAAAAAAAAAAAAAAAA

So uh.
I sat down to “add some sounds.”

Anyway, I guess I wrote a manual audio engine.

Like.
A whole one.

From scratch.

Why?
I don’t know.
Ask the version of me who thought “hm yes, procedural rolling‑bearing noise with dynamic playbackRate modulation sounds fun.”

Stuff I Did (aka: why am I like this)

  • Built a full WebAudio pipeline with:

    • master gain
    • compressor
    • noise generator
    • rolling loop buffer
    • bandpass / highshelf filters
    • waveshaper distortion
    • dynamic envelopes
    • speed‑scaled playback
    • and a tiny sprinkle of “why didn’t I just use .wav files”
  • Added per‑event sound design:

    • grab -> crunchy mechanical click
    • release -> softer mechanical click
    • snap -> UI‑meets‑industrial “thunk”
    • spin -> tick‑tick‑tick with speed‑based pitch
    • stop -> inertia decay with settling noise
    • rolling -> continuous bearing rumble with dynamic stuff
  • Implemented a rolling sound layer that:

    • loops seamlessly
    • crossfades
    • modulates playbackRate based on speed
    • tears itself down when idle like a polite guest
  • Added speed‑aware feedback everywhere so the widget feels alive and so realistic.

Features

  • Rolling bed sound with adjustable intensity (rollSoundLevel).
  • Speed‑scaled spin ticks so fast spins sound fast and slow spins sound like a sleepy hamster wheel.
  • Grab/release/snap/stop events that feel like a physical object instead of a div.
  • Automatic fade‑outs, teardown, and cleanup so your CPU doesn’t cry.
  • Configurable everything because apparently I can’t stop adding knobs.

A Bug I Fixed (and by “bug” I mean “my sanity”)

  • Prevented the rolling layer from stacking 400 overlapping loops because I forgot to gate it.
    (It sounded like a jet engine and now my ears hurt.)

Demo

Updated the demo to expose rollSoundLevel because if I suffered for this, you get to play with the dial.

1

Comments

NellowTCS
NellowTCS about 2 months ago

Yes I’m serious, I actually forgot that music files existed and that people have made audio clips of trackballs before.

NellowTCS

DEVLOG TIMEEEEEEE
Hackatime is behaving much more nicely, kudos to the maintainers. XD

I did stuff.

Okay, actually, I turned the little trackball experiment into a proper, modular toolkit with theming, config, and haptics.

Stuff I did

  • Added a config/type system so users can pass options and they merge with sensible defaults (types.ts, config wiring in main.ts).
  • Theme system: JSON themes + a loader, added multiple theme files (default, aqua, glossy, metal, red) and wired them to CSS variables so themes actually apply
  • Scroll mode & utilities: initial scroll helpers landed (page/nearest/horizontal/momentum), plus a scroll engine to drive snapping and scroll behavior
  • Modularization: split the code into small modules: DOM manager, controls manager, physics loop, scroll engine, sound, and haptics, so each piece is easier to reason about and test
  • Haptics: integrated the tactus package and added a simple haptic engine that calls triggerHaptic(duration)
  • Auto-reconfig: when config changes after mount, things reapply (theme, physics params), so runtime updates are respected (main.ts, physicsEngine.ts).

Features (what you, yes you John, get now)

  • themes loaded from JSON.
  • CSS-variable theming so switching a theme is immediate and global.
  • Trackball scrolling with multiple scroll modes and snap behavior.
  • Haptics through tactus for native-feeling pulses.
  • Auto reconfiguration so changing options at runtime updates behavior.

A bug/UX fix

  • Made theme application safe and non-destructive so it won’t “explode” rendering; also added a small delay/guard where needed so controls hide/show behavior is nicer.

Demo (cause we all need one in our lives dont we)

  • Updated the demo to show the new stuff using something really cool hehe
Attachment
Attachment
Attachment
Attachment
Attachment
Attachment
0
NellowTCS

So first devlog for Tamaru!!!

I initially made this today at like 6 AM as a small JS experiment, then was like, this is actually so useful!!!

Then i decided to make this an npm module/library, for fun :3.

Stuff I did to the original JS module

  • Made into TypeScript to be cleaner (almost just a rename to .ts, not much complexity)
  • MODULARIZATION: core movement/physics, DOM helpers, and entry point.
  • Styles are imported as text and injected at runtime for a self-contained build (I don’t like having to import styles separately for a few reasons)
  • Used tsup for TypeScript bundling.
  • Symlinked Build/dist to Demo/dist for easy demo updates. (so useful omg i should do this everywhere)
  • Copied a bunch of CI, Config, etc from another project, Sairin, and edited it for Tamaru
  • Created Demo/index.html to showcase the widget.
  • Added a large block of lorem ipsum in a scrollable div to test trackball scrolling.
  • Disabled native page scrolling; only the trackball or scroll area can scroll content.

Features

  • pseudo-3d ball
  • pseudo-3d ball (it’s so cool omg)
  • Draggable, floating widget with snap-to-edge logic.
  • Trackball area for scrolling with inertia.
  • Minimize/restore toggle.

A bug that was fixed

  • Controls popup uses a delay before hiding, so i don’t have to speed click before the controls hide.
Attachment
Attachment
Attachment
Attachment
1

Comments

NellowTCS
NellowTCS 2 months ago

i used the demo page to make the banner
that’s so funny to me
it’s just the Tamaru with the orb and it’s perfect