Best way to make my “web os” feel like a real app? Disabling user text selection (seriously 😭)
I started building wobblyOS!, a jellified web-os with some fancy animations that you will all be delighted to see. (using HTMX and github pages)
How wobblyOS windows feel alive
The goal of this system is not to “animate” windows with CSS transitions. It treats every window as a physical object with mass, velocity and momentum, then simulates that every frame.
This makes movement feel weighted, reactive, and surprisingly satisfying.
The spring
Every window tracks two positions- where it is, and where it’s going:
currentX += (targetX - currentX) * 0.18
That 0.18 is the spring stiffness.
Velocity
After each position update, velocity is derived from the gap that remains:
velocityX = targetX - currentX
This is reused for every visual effect.
Rotation
The window tilts based on how fast it’s moving horizontally:
rotate = velocityX * 0.08
Drag it left and it leans left. Etc etc.
Squash & stretch
Animation principle, applied physically:
stretchX = 1 + abs(velocityX) * 0.0015
stretchY = 1 - abs(velocityX) * 0.0008
Fast horizontal movement makes it wider and slightly shorter.
Bounce
A sine wave modulated by current velocity adds a subtle wobble while moving:
bounce = sin(time * 0.015) * min(speed, 25) * 0.15
When still, it does nothing- moving, it oscillates.
Open & close
A second spring drives the scale when windows appear or disappear:
velocity += (target - value) * stiffness
value += velocity
Open: spring launches from 0 with an initial velocity kick, overshoots 1, bounces back.
Close: target flips to 0, the window squashes down toward the taskbar and vanishes.
Taskbar
Waits 500ms after the window is gone, then pops in on its own spring. Restore reverses it- button springs out first, then the window pops back.
Summary
Every effect shares one velocity value derived from one spring equation. No CSS transitions, no keyframes, just position, momentum, and a multiply.
How wobblyOS windows feel alive
The goal of this system is not to “animate” windows with CSS transitions. It treats every window as a physical object with mass, velocity and momentum, then simulates that every frame.
This makes movement feel weighted, reactive, and surprisingly satisfying.
The spring
Every window tracks two positions- where it is, and where it’s going:
currentX += (targetX - currentX) * 0.18
That 0.18 is the spring stiffness.
Velocity
After each position update, velocity is derived from the gap that remains:
velocityX = targetX - currentX
This is reused for every visual effect.
Rotation
The window tilts based on how fast it’s moving horizontally:
rotate = velocityX * 0.08
Drag it left and it leans left. Etc etc.
Squash & stretch
Animation principle, applied physically:
stretchX = 1 + abs(velocityX) * 0.0015
stretchY = 1 - abs(velocityX) * 0.0008
Fast horizontal movement makes it wider and slightly shorter.
Bounce
A sine wave modulated by current velocity adds a subtle wobble while moving:
bounce = sin(time * 0.015) * min(speed, 25) * 0.15
When still, it does nothing- moving, it oscillates.
Open & close
A second spring drives the scale when windows appear or disappear:
velocity += (target - value) * stiffness
value += velocity
Open: spring launches from 0 with an initial velocity kick, overshoots 1, bounces back.
Close: target flips to 0, the window squashes down toward the taskbar and vanishes.
Taskbar
Waits 500ms after the window is gone, then pops in on its own spring. Restore reverses it- button springs out first, then the window pops back.
Summary
Every effect shares one velocity value derived from one spring equation. No CSS transitions, no keyframes, just position, momentum, and a multiply.
I started building wobblyOS!, a jellified web-os with some fancy animations that you will all be delighted to see. (using HTMX and github pages)
The goal of this system is not to “recognise” a star visually. It turns both your drawing and a perfect star into comparable point data, then measures how far apart they are.
This makes scoring fast, deterministic, and surprisingly stable.
While you draw, the app records raw mouse positions:
path = [(x1, y1), (x2, y2), etc etc]
This data is noisy and depends heavily on speed and polling/sampling rate.
The stroke is converted into a fixed number of evenly spaced points (120).
This removes:
drawing speed differences
uneven mouse sampling
Now every drawing has a consistent structure.
The shape is transformed so comparison is fair:
centred around (0, 0)
scaled so max radius = 1
This removes position/size- only shape remains.
A perfect 5-pointed star is generated mathematically using polar coordinates and sine-based radius switching.
It is also sampled into 120 points so it matches the user format.
Each user point is compared directly to the corresponding star point-
error += distance(user[i], star[i])
This produces an average geometric deviation across the full shape.
Final score is computed as:
score = 100 - error * 120
Then clamped between 0 and 100.
The system works by converting both shapes into normalized point sequences and measuring their geometric distance.
The goal of this system is not to “recognise” a star visually. It turns both your drawing and a perfect star into comparable point data, then measures how far apart they are.
This makes scoring fast, deterministic, and surprisingly stable.
While you draw, the app records raw mouse positions:
path = [(x1, y1), (x2, y2), etc etc]
This data is noisy and depends heavily on speed and polling/sampling rate.
The stroke is converted into a fixed number of evenly spaced points (120).
This removes:
drawing speed differences
uneven mouse sampling
Now every drawing has a consistent structure.
The shape is transformed so comparison is fair:
centred around (0, 0)
scaled so max radius = 1
This removes position/size- only shape remains.
A perfect 5-pointed star is generated mathematically using polar coordinates and sine-based radius switching.
It is also sampled into 120 points so it matches the user format.
Each user point is compared directly to the corresponding star point-
error += distance(user[i], star[i])
This produces an average geometric deviation across the full shape.
Final score is computed as:
score = 100 - error * 120
Then clamped between 0 and 100.
The system works by converting both shapes into normalized point sequences and measuring their geometric distance.
deployed a prototype build of “draw a perfect star” to github pages! also made it look a little nicer lol :p
deployed a prototype build of “draw a perfect star” to github pages! also made it look a little nicer lol :p
Built a first prototype for my “draw a perfect star” online game! Like if you could do better than me LOL
Built a first prototype for my “draw a perfect star” online game! Like if you could do better than me LOL