Repository navigation
How It Was Built Part 3
Part of How OpenShaker was built
Every game shares its data in its own way. OpenShaker only uses what the games offer out of the box: nothing is installed into any game.
September 10
Problem. Does every game need its own way of turning data into vibration? The maintainer asked whether "the algorithm to convert the data to sound is different for each game".
What we found. HaptiConnect uses one shared set of effects with a thin adapter per game (seen from outside). The games differ only in how they deliver the data (network packets, shared memory, a plugin's stream), in their units, and in which values they have.
Fix. OpenShaker has one reader per game. Each turns its game's format into one common frame, which is all the shared effects ever see: speed in m/s; g-forces in the car's own directions; slip scaled so 1.0 is about the grip limit; gears as -1 for reverse, 0 for neutral and 1 and up for forward; and wheels always in the order front-left, front-right, rear-left, rear-right.
Forza Motorsport and Forza Horizon (setup and effects: Forza Motorsport, Forza Horizon 5 and 6)
September 10
Problem. Forza sends packets of different sizes, with the gear and pedal values in different places.
What we found. There are three layouts: 232 bytes ("Sled", motion only), 324 bytes (Horizon 4, 5 and 6) and 331 bytes (Motorsport's "Car Dash"). The dashboard part starts at byte 244 in Horizon packets and at byte 232 in Motorsport's. The first live Motorsport run delivered about 167 packets a second.
Fix. The reader tells the layout from the packet's length alone. The in-game setting is Data Out on, IP 127.0.0.1, port 5555.
September 10
Problem. On Sled, the gear-shift and pedal effects could never fire, in either app. Yet HaptiConnect's own setup guide tells Motorsport players to pick Sled.
What we found. The 232-byte Sled packet simply has no dashboard part.
Fix. The maintainer switched Motorsport to Car Dash. The README warns against Sled, and the drive checker rejects Sled recordings.
September 10
Problem. During the first measuring run, HaptiConnect's output kept cutting out.
What we found. HaptiConnect's Motorsport port received 618 packets in four seconds from the game itself, sitting at its menu with the race off. HaptiConnect was switching between the game's stream and ours, which would have spoiled the whole run.
Fix. The maintainer moved the game's data to OpenShaker's port 5555. A gotcha: Forza only applies the change after you leave its settings screen. OpenShaker also ignores packets marked "race off".
September 10
Problem. On the first live Forza drive, the maintainer didn't feel the kerbs: "Okay, I played a bit. I didn't really notice curbs".
What we found.
- The data was arriving: the log had 39 kerb episodes.
- OpenShaker's kerb buzz sat at about 90 Hz, under the engine, and too high to feel well.
- Decoding the raw packets showed that Forza's per-wheel surface value (a surface-type code, sent next to the kerb flag) reads 1.5 to 2.6 on Motorsport's kerbs. OpenShaker's reader was clipping it to 1.0.
Fix. The reader keeps the real surface value. The kerb buzz moved down into the range a shaker plays well, and it was later rebuilt from HaptiConnect's measured behaviour (HOW_IT_WAS_TUNED, Chapter 2). The lesson stuck: real laps, not test signals, became the reference.
September 10
Problem. One gear change could give two thumps.
What we found. Forza numbers reverse as 0 and neutral as 11, and a shift can pass through neutral on its way. Once we measured it, HaptiConnect's Forza plugins turned out to fire on nearly every gear change, so a Forza shift through neutral gives two thumps there too.
Fix.
- The reader maps Forza's numbers to the common scheme.
- The gear-shift effect has settings for neutral, so each game can do what HaptiConnect's plugin for it does: the Forza profiles play both thumps, as HaptiConnect does.
- The same mapping, run backwards, later bit the replay tool: it sent neutral as reverse. That was fixed on September 18, with a test that sends every gear there and back.
September 18
Problem. The gear number can jump across a pause, a rewind or a respawn, which would play a thump HaptiConnect doesn't play.
Fix. After a gap in the data, the next frame only records its gear and plays nothing.
September 19-20
Problem. The road rumble matched HaptiConnect on some cars and not on others (on one 9000-rpm car it was about 9 dB too strong). Pauses and race starts could read as bumps.
What we found.
- HaptiConnect's road rumble follows the suspension travel in metres, not the percentage the game also sends.
- In the air, the percentage reads 0 while the metres keep moving.
- The travel jumps at a race start and after a pause.
Fix. The reader keeps both measures. The Horizon profile follows metres (Motorsport's waits for another change; see what is still open). Airborne frames keep the history going, speeds are never worked out across a gap, and repeated packets are recognised by Forza's own clock.
September 18
Problem. The maintainer drove over kerbs in BeamNG and in both Horizon games, but the Horizon recordings showed none: "maybe they just don't have bumps like ACE".
What we found.
- An independent decode of the raw packets (its byte positions checked first on speed, rpm and gear) showed the rumble-strip flag at 0 on every packet, for every wheel, on every Horizon lap.
- The "roughness" value turned out to be a surface-type code.
- HaptiConnect's Horizon 5 plugin plays no kerb buzz there either, only a stronger road rumble on rough surfaces.
- A skeptic agent worked it out again and reached the same answer.
Fix. No kerb effect for Horizon: the maintainer's guess was right. The stronger rumble on rough ground is a known gap (per-game gaps). BeamNG has no kerb data either (BeamNG has no kerb data).
September 11
Problem. The maintainer wanted separate settings for the two Horizon games: "separate the Forza games as well".
What we found. Both games send byte-identical packets to the same port. HaptiConnect has the same limitation, which is why Horizon 6 couldn't borrow its Horizon 5 plugin (No Forza Horizon 6).
Fix. OpenShaker asks Windows which Horizon program is running. It uses a direct process snapshot, because the usual command-line tool would flash a console window on every check. The check takes a few milliseconds and is cached for 5 seconds. If neither game is visible it keeps the current Horizon preset, and if both run, Horizon 5 wins. Checked live.
September 12
Problem. A public app shouldn't accept game data from the network by default, but some players run Forza on an Xbox or a second PC.
Fix. Every listener binds to this PC only (127.0.0.1). The advanced settings can open Forza to the network (Windows Firewall must then let OpenShaker through). A test pins the local default.
Assetto Corsa EVO (setup and effects: Assetto Corsa EVO)
September 10
Problem. The ACE reader was first built against a fake memory block written from the documentation, so its names, positions and ranges were unconfirmed.
What we found. On the first live attach the game reported "live", and a 340-second drive was logged. A later capture showed ACE updating its data a little over 300 times a second.
Fix.
- The reader only opens memory the game has already created, and never reads past its real size, so the older Assetto games' smaller blocks are safe.
- It lets go after 5 seconds without new data.
- Since September 18 it logs the whole 800-byte page, so logged drives can be replayed exactly.
- It falls back to the old names for the older Assetto games (Assetto Corsa and ACC).
September 10 to 18
Problem. For its first week, ACE had no profile of its own. The automatic game-to-profile map sent both Forza and ACE to the Forza Motorsport profile.
Fix. On September 18 ACE got its own profile, starting as a copy of the Motorsport one, and a one-time switch moved the existing ACE settings over to it. It was then changed wherever HaptiConnect's own ACE plugin measurably differs (ACE gears, ACE's own vibration hints).
September 10, then September 18
Problem. Shifts in ACE thumped twice.
What we found.
- ACE counts 0 as reverse, 1 as neutral and 2 as first.
- Every shift shows about 250 ms of neutral.
- September 10: a short neutral window stopped the double thump: 14 thumps for 14 shifts.
- September 18: ACE's new profile, copied from Motorsport, also copied HaptiConnect's Forza habit of thumping into neutral and again into gear, so it thumped twice again. HaptiConnect's own ACE plugin thumps once, when the next gear engages (measured here).
Fix. The reader subtracts one. The ACE profile fires only when a gear engages, never into neutral.
September 10
Problem. The slip tone played in ordinary corners.
What we found. ACE's slip reads about 0.05 even while the tyres grip. Forza's reads 0 until grip is lost.
Fix. ACE's slip is rescaled so 1.0 means about the grip limit, and ignored when the car is nearly stopped. Afterwards the slip tone played only in real wheelspin and slides.
September 10
Problem. On ACE, the road rumble carried about 45 % of the output energy, against 20-27 % in HaptiConnect's Forza output.
What we found. ACE reports the suspension travel in metres, and it moves far less than Forza's 0-1 range.
Fix. A per-game setting turns ACE's metres into a Forza-like scale. It was refined over several rounds against recordings.
September 18
Problem. The maintainer felt Forza Motorsport was less impressive than ACE: "I'd say the feedback felt less impressive than EVO".
What we found. ACE publishes its own vibration hints for kerbs, slip, road and ABS. Forza has none of these. And the refitted Motorsport profile keeps the road rumble almost silent on smooth tarmac, just as HaptiConnect does.
Fix. OpenShaker uses ACE's kerb and road hints. The ACE profile has no kerb cycle and no shift light, because HaptiConnect's ACE plugin has neither.
September 18
Problem. What happens with a game we never tested? In the maintainer's words: "what happens if we boot up a game we haven't tested, like ACC?"
What we found. The older Assetto games still use the old memory names, and the start of their layout is the same. So the engine, gear shifts, acceleration, suspension and crashes should work; slip, lock, ABS and kerbs won't.
Fix. The README lists them under Should also work (untested).
BeamNG.drive (setup and effects: BeamNG.drive)
September 12
Problem. A logged BeamNG drive showed the brake at 0 the whole way. In the maintainer's words: "let's fix the BeamNG brake first".
What we found. Checked against BeamNG's own source, the pedals sit at bytes 48, 52 and 56. The first-day reader looked 8 bytes too late, so its "throttle" was really the clutch. Decoded correctly, the brake had been pressed for about 30 % of that drive.
Fix. Corrected, with a test pinned to a real packet from that drive. The handbrake, ABS, traction control and engine-off now come from the dashboard lights.
September 12
Problem. On BeamNG, crashes, bumps, acceleration and wheel lock were impossible: every packet was the plain version, without g-forces.
What we found.
- HaptiConnect gets BeamNG's extra data from a small mod it installs into the game.
- BeamNG's own log shows that its 0.39 update disabled all mods.
- That mod was out of date anyway: it never filled in the road speed.
- Claude started writing a replacement mod, and the maintainer stopped it: "Why are we making a mod? I don't want to really install stuff like that." The maintainer was also thinking of putting the tool on GitHub.
- Claude agreed that a mod had been "the shortest path from where the code stood, not the best one".
- The maintainer's early "The BeamNG plugin however works great" probably came from driving before that update.
Fix. From then on, the rule for every game was built-in options only: Forza's Data Out; BeamNG's OutGauge and Motion Sim; ACE with no setup at all; and Trackmania through Openplanet's own plugin manager.
September 12
Problem. OutGauge has the pedals, rpm, gear, dashboard lights and wheel speed, but no g-forces or true ground speed. Motion Sim, BeamNG's output for motion rigs, has the motion but no engine data. In the maintainer's words: "go ahead with BeamNG Motion Sim".
Fix. One port receives both; packets are told apart by size and tag, and each updates its half. A half that goes quiet is dropped. Ground speed and wheel slip come from combining the two. If one output is missing, the status line says which box to tick in BeamNG's options.
September 12, then September 18
Problem. Motion Sim's axes are smoothed and flipped compared with the old mod, so directions and thresholds were guesses.
What we found. A checker tool replays a drive and reports which axis follows the car's change of speed. The first drive had no Motion Sim data at all: the setting was either never switched on or not saved. On the second drive the forward/backward axis checked out.
Fix. Forward/backward is verified, sideways only weakly, and vertical no further than plausible sizes. The thresholds are still open (per-game gaps).
September 12
What we found. The "speed" field is wheel speed, not ground speed. The old mod could leave the ABS light stuck on. And BeamNG lights the battery warning when the engine is off.
Fix. Wheel speed is compared with Motion Sim's ground speed to detect lock-ups and wheelspin. ABS counts only while braking. Engine-off is taken from the battery light.
September 12
Problem. Some BeamNG effects had only been silent because the pedal data was wrong. With correct pedals, the shift light would have pulsed about 11 dB over everything else.
Fix. Those effects got explicit, scaled strengths in the same change.
September 18
What we found. Neither BeamNG output has a kerb or surface value. Kerbs arrive only as small vertical jolts, below HaptiConnect's bump threshold, so neither app pulses on them.
Fix. The thresholds stay where they are, so OpenShaker doesn't add pulses HaptiConnect doesn't play.
Trackmania (setup and effects: Trackmania)
September 12
Problem. Trackmania doesn't send its data like the other games. In the maintainer's words: "If I could feel how close a car was to slipping in Trackmania".
What we found. Openplanet, the community's scripting platform for the game, has a plugin called Data Sender. It streams the car's full state to a local connection.
Fix.
- OpenShaker connects to it, waits quietly while it isn't there, and reconnects by itself.
- Installing Openplanet and Data Sender is the one extra step, done in Openplanet's own plugin manager. Some competitions switch plugins off.
- Later the maintainer wondered about writing our own: "I almost wonder if we can make a Trackmania plugin ourselves". Claude recommended staying with Data Sender: it gives the same data, an Openplanet plugin's audio can only reach the game's own output, and a custom plugin would need publishing and upkeep. The idea stays open (ideas saved for later). The Trackmania guide has the details.
September 12
Problem. Data Sender wasn't installed yet when the Trackmania support was written.
What we found. So it was built from the plugin's published source and tested only against a fake server and a scripted rehearsal. One behaviour, the slip value reading 0 while the tyres grip, was inferred from another plugin's source.
Fix. The first real drive, on September 18, confirmed that behaviour and found the problems in the entries below.
September 18
Problem. After installing Data Sender, nothing arrived. Its service was stopped, and even when running it sent only 10 updates a second.
What we found. Its source shows three things: it starts switched off, its default interval is 100 ms, and it accepts commands even while stopped.
Fix. OpenShaker starts a stopped service and lifts only the shipped 100 ms default to every frame. It never overrides a setting the user chose, it logs one line when it does this, and the README says so. These conditions were agreed before the code was written.
September 12, then September 18
What we found. Most values come in two layouts. The sideways-speed value only works in Openplanet's developer mode. Per-wheel ground contact isn't sent in Trackmania (2020).
Fix. OpenShaker accepts both layouts, works out the sideways speed itself, and tells whether a wheel touches the ground from each wheel's "falling" state. Logged drives keep every value.
September 18
Problem. After the maintainer raised the update rate ("I was able to increase update frequency"), the data stopped for about 0.2 seconds every second. Then Claude's checker was refused.
What we found.
- A per-client cap of 200 messages a second had been left in Data Sender's settings by an older OpenShaker build. Above that rate, the game hit the cap and blacked out.
- Separately, the plugin's client limit had been set to 1.
Fix. The maintainer reset both settings, the client limit back to its default of 8 ("done, set to 8"). OpenShaker no longer sends the global rate command, and it turns off the plugin's chatty status messages for its own connection. The README's troubleshooting covers the client limit.
September 12
What we found. The game sends surfaces only as numbers. The table comes from the list in Openplanet's own header file. Two look-alike tables in the same header would have been wrong; one of them isn't a surface list at all.
Fix. The list is pinned by a test. ID 80 means the wheel is in the air.
September 12
Problem. The effects run every 10 ms and see only the newest frame, but the plugin sends frames faster, so a crash could slip between two looks.
Fix. Acceleration is measured as the change in velocity over at least 20 ms, and a long gap clears the history. It became a project rule: fast data sources must not rely on frame-to-frame differences.
September 12
Fix. The reader learns each wheel's rolling size while the car drives straight, then compares the wheel's rotation with the road speed.
Built September 12, first driven September 18
Problem. The maintainer wanted to feel the car approaching a slide. On the first real drive, the warning pulsed in almost every corner: "pulsing in corners was constant and annoying, landings felt good".
What we found.
- The game's slip value is 0 while the tyres grip and jumps to full once they slide, so it marks the slide, not the approach. The warning was built instead from sideways g-force and the car's body angle, with first-guess limits of 20 m/s² and 6 degrees. It starts at 60 % of the limit, which is 12 m/s².
- The drive showed the car holding 60-90 m/s² sideways on tarmac without letting go, so the warning ran about 22 % of the time.
- Most "slides" were really wall hits or landings.
Fix.
- The maintainer chose "Loose surfaces" only. The warning now works on dirt, grass, sand, snow, ice and wet roads, with per-surface limits that are still first guesses (Trackmania gaps).
- The g-force rumble is off in the Trackmania profile, and the slide buzz is softer.
- The maintainer also moved three sliders: engine rpm down, g-force rumble off, gear shift up.
September 18
Problem. Flying and landing felt harsh: "The flying and landing were a little harsh".
What we found. In the air, the dampers stretch fully within about 80 ms, which read as bumps, and the wheels spin freely, which read as wheelspin. About 30 % of take-offs made a bump.
Fix. The suspension is held while a wheel is in the air and settles after landing, and airborne wheels report no slip. Take-off bumps dropped from 30 % to 9 %. Gentle touchdowns now give a small bump sized to the real compression, where before they were either silent or a full hit on all four wheels.
September 18
Problem. Every respawn fired a full-strength crash, and a gear-change thump as well.
What we found. An adversarial review of the logged drive found it. A respawn teleports the car, so its speed jumps from 0 to, say, 55 m/s from one frame to the next, which looks like a colossal crash. The plugin's "discontinuity" counter changes on a frame where the speed is still 0, and the new speed arrives about 14 ms later.
Fix. After the counter changes, the car is kept quiet for 60 ms. The next drive had 8 respawns and no false crash.
September 18
Problem. Some landings played a crash hit on top of the landing thump.
What we found. When the ground stops a car that is tipped nose-down, the stop reads as braking or cornering in the car's own directions. A slope pushes partly sideways too. On the second drive, 3 of 13 landings misfired this way.
Fix. Two separate fixes, one for nose-first and one for slope landings. For a moment after each real landing, the reader removes the ground's push, then fades it back in (a hard cut was a jolt of its own). It isn't done all the time, because on loops and wall rides those forces are real. Result: 0 of 24 landings misfired across both drives, and real crashes still fire.
September 18
Problem. OpenShaker has a tool that suggests thresholds from your own drives. Its first suggestions for Trackmania looked tempting.
What we found. Replayed against the real drives, they failed: on the first drive, the suggested crash threshold would have dropped 4 of its 14 real wall hits; the bump suggestions were unstable between the two drives; and there were too few slides on loose surfaces (about 10 seconds per surface) to learn from.
Fix. Nothing was applied. The first guesses stay until there is more data.
September 18
Problem. The tuning tool wrote each car's rev range and suspension travel into the user's settings file.
Fix. These are the same for every player, so they now ship in the Trackmania profile, one entry per car type.
Built September 12, first felt September 18
Problem. The maintainer wanted Trackmania to be complete: "I'd like to make this fully featured."
Fix. New effects: surface textures, water, roof scrapes, landing thumps sized by the fall speed, and turbo and reactor boosts. They are on only in the Trackmania profile, so the games matched to HaptiConnect are untouched. The first verdict: "landings felt good". The effects overview leads to each one.
September 10
Problem. The maintainer asked for a desktop shortcut: "I'd like a shortcut on my desktop, make an icon." Double-clicking it printed a Python error: "No module named 'numpy'".
What we found. The first suspect was the wrong Python. The real cause was Claude's sandbox: Claude's install had gone into a private, redirected copy that the maintainer's Python never saw (Traps in Claude's own workspace).
Fix. A dedicated Python environment outside AppData, which the launcher calls by its full path. Checked by launching it the way Explorer does.
September 10
Problem. "Give me an application with a simple UI please"
Fix. A simple window: live game data, an output meter, a master slider, an on/off switch and a strength per effect, a test tone and a demo. Asked whether it would feel like HaptiConnect, Claude said no: the effects matched by name, but the sound was its own first guess. That answer started the measuring work (HaptiConnect becomes the measuring stick).
September 11
Problem. The maintainer asked: "is the GUI part of the application all finished?"
What we found. Checking before answering, Claude found three problems:
- "Save settings" would have copied the whole calibration into the user's settings, so any later recalibration would silently never apply.
- "Reset" went back to generic defaults, about six times HaptiConnect's level.
- The strength sliders ran from 0 to 2, while the useful range was 0.12-0.2, only a few pixels wide.
Then came the overclaim: Claude said it had made the fixes when it hadn't (When the AI got it wrong).
Fix. Each effect got a trim, where 100 % means exactly as calibrated, and only the user's own changes are saved. The sliders read 0-200 %, and "Reset to profile" restores 100 %. Old settings were migrated with a backup. The tests caught two regressions on the way.
September 11
Problem. The shortcut started the app, but no window appeared. The only trace was an error log.
What we found. A Python environment on Windows doesn't carry the library files the window toolkit (Tcl/Tk) needs.
Fix. The app points the toolkit at the main Python's files before it opens a window. The same trap came back when building the installer, where the packager silently dropped the toolkit. The build now fails loudly if it is missing.
September 11
Problem. The maintainer wanted the app to start with Windows into the notification area: left-click to open, right-click for status and actions. There was also a stray window: "When I open the app there is a cmd window that opens in the background."
What we found. The first cause was simple: the shortcut ran a batch file. The deeper cause is in The command window that kept coming back.
Fix. A tray icon, in colour while running and grey while stopped. The right-click menu has live status lines (device, game and frame rate, profile, level, and a "Problem" line only when something is wrong). Closing the window hides it; only Quit exits. A windowless launcher replaced the batch file.
September 11
Problem. Found in testing, before the maintainer used it: every tray menu action would silently do nothing.
What we found. The tray and the window run on different threads, and the window toolkit may only be touched from its own thread. The error was swallowed.
Fix. Every tray action is handed to the window's thread through a queue, and the test tone got the same fix. Menu checkmarks are rebuilt after every change, because Windows reads them only when the menu is built.
September 11, hardened September 12 and 18
Problem. A second launch would start a second copy, fighting the first over the ports and the shaker.
Fix.
- A tiny local connection acts as both a lock and a message channel: a second launch just says "show" and exits. It is local-only, so there is no firewall prompt.
- Later additions: a "quit" message for the installer, and a lock the installer checks.
- The pre-publish audit found that a launch that could neither take the lock nor reach the running copy did nothing at all. Now it logs and shows the reason.
September 11
Problem. The maintainer only opens the app to drive, so Start and Stop were one more thing to get wrong: "I just want it to go".
What we found. Without a Start button, a failed start would leave the app dead. And the shaker is often still asleep when Windows boots.
Fix. Haptics start about 0.4 seconds after launch and retry every 15 seconds while they can't. The status line says why they aren't running and that they are retrying. A hidden tray app never opens an error box nobody could see. A test fakes a dead device and checks that the retry fires. A week later the maintainer asked for an off switch after all (A Haptics on switch after all).
September 11
Problem. There was still a command window when opening the app from Start: "I still have a cmd window that opens".
What we found.
- The windowless Python inside a Python environment is only a stub: it starts the console version of Python, and Windows gives that its own window.
- The launcher is therefore two processes. Ending "the app" by program name can leave the other half running, still holding the shaker and the game ports.
- The Start menu had no entry at all; Start search had been finding the desktop shortcut.
- During the hunt, Claude's process filter matched its own shell and ended it.
Fix. A repair step replaces the stub with the real windowless Python, and three tests cover it. The maintainer's verdict: "no cmd window now". The installed app isn't affected.
September 10 to 11
Problem. It started on September 10 with a request: "I'd want the desktop app to auto detect which game I have open". Switching presets per game was added that day. On September 11 the maintainer found the settings buttons clunky, and wanted each game's settings to load by themselves and to be editable without the game running: "The save settings, load profile and open folder is too clunky."
What we found. Switching games only replaced the settings the new game defined, so the previous game's levels leaked through: the engine was about 12 dB off after a switch from Forza to BeamNG.
Fix. One preset per game, with a picker that follows the running game. Edits save themselves. Switching starts from clean defaults. Checked live: a Forza change left ACE untouched.
September 11 to 20
Problem. Several separate bugs:
- Turning Master down wasn't saved, so it could come back at full strength after a reboot. The audit called this a safety issue.
- The Advanced dialog's OK button saved nothing.
- A "no" to starting with Windows would have been asked again.
- Every default was frozen into the user's settings file.
- Presets stored copies of the on/off switches, so profile changes never reached them.
Fix. Master saves shortly after a change. The settings file holds only what differs from the defaults. A one-time cleanup, approved by the maintainer and tried first on a copy of their real settings, removed the copied switches and kept their deliberate "off"s.
September 11 to 18
Problem. The maintainer wanted the app to start with Windows, and later to find it by typing its name in Start: "Please make it so the app is searchable from the start menu."
What we found. Claude's sandbox also redirects the registry, so Claude couldn't set up the startup itself. And for other users, the advice for the release was: ask before changing their system.
Fix. The app registers its own startup entry the first time it is opened. Other users get a one-time question; in the installed version, the installer's ticked box is the consent. An old entry under the app's former name is replaced, so two copies never start. The maintainer's Start menu entry was created by a script started through Explorer.
September 18
Problem. A week after the haptics became always on, the maintainer asked for an off switch: "Please add a toggle in the program and on the tray item to disable haptics".
What we found. A four-reviewer check found an older bug. If another program held the shaker when OpenShaker started, the start failed halfway and left the game ports blocked until Quit, so switching off and on again could never recover. A second review found that the cleanup froze the window for about a second.
Fix. "Haptics on" in the window and the tray. Off frees everything, turns the icon grey, and is remembered. The app now opens the shaker first, so a busy shaker fails with nothing to undo, and any half-start is cleaned up. Before restarting the maintainer's copy, Claude waited for their Trackmania drive to end.
September 11 and 18
Problem.
- Recording comparisons needs the game ports free, but the documented option to start without haptics did nothing.
- There was no way to quit the tray app from the command line.
- HaptiConnect sat in Windows' Startup folder and grabbed BeamNG's port at every boot, so whichever program started first got the data: a coin flip.
What we found. The retry timer ignored the launch option and started the haptics a fraction of a second later anyway.
Fix. The option works now, with a test. A quit command waits until the ports are free, and the installer uses it, timed so the installer's own checks see the app gone. HaptiConnect's startup entry was later found disabled. The README's troubleshooting and the guide's troubleshooting page name HaptiConnect and SimHub as the usual port conflicts.
September 18
Problem. Stopping a logging run the hard way lost the whole drive, and the log's summary never listed its sources.
Fix. The data streams to disk as it arrives, and the summary is written at the start and at the end. Existing log folders are protected from being overwritten. The console logger also refuses to run beside the tray app, instead of recording an empty drive while the tray app holds the ports.
September 18
Problem. The maintainer wanted a click on a slider to jump straight there, and a way to type a percentage: "if I click on the sliders it jumps to that percentage".
What we found. On Windows, sliders only creep toward a click. A review of the new code with several agents found 14 bugs, such as the arrow keys on Master jumping to 0 or 100 %.
Fix. Click to jump, type to set, and arrow steps of 1 % and 10 %. All 14 bugs were put back one at a time, and the tests caught each one.
September 11
Problem. Pressing Test tone gave no vibration: "when I press test tone I get no vibration". Hours later: "Demo works but the test tone doesn't".
What we found.
- First, a wrong conclusion. A recording of the shaker's output showed the tone at full level, so Claude blamed the amplifier.
- The Demo report proved the hardware fine. Windows lists the ButtKicker four times, once under each of its audio systems.
- When the preferred one wasn't ready yet (at boot, before the shaker woke up), the app took the first match, which opened an exclusive stream. The haptics played, but the test tone's second stream could never open, and HaptiConnect couldn't share the device either.
- The recording tool only sees the shared mixer, so an exclusive stream is invisible to it. That is why the hardware had looked at fault.
Fix. Only shared audio systems are used; if none is ready yet, the app waits and retries. Devices are looked up again on every start. The test tone is mixed into the running stream instead of opening a second one. The verdict: "it works now".
September 12
Problem. Found while preparing for strangers: if the shaker was missing, the original code played through Windows' default output. On someone else's PC, that is their speakers. Device names also change after a re-plug ("2- ButtKicker").
Fix. The shaker is picked by name, ignoring the number in front. A missing shaker is reported ("pick your shaker under Output > Device"), and nothing else plays. The default output is used only if chosen on purpose. The README's Safety section and the guide's safety page explain this. HaptiConnect's own trouble came from the same kind of cause: remembering the device by ID (HaptiConnect kept forgetting the shaker).
September 18
Problem. Found by the pre-publish audit: unplugging the shaker while playing left the app saying "Running" while nothing played.
What we found. When a device disappears, the audio library raises no error. The stream just stops.
Fix. A watchdog notices a stream that has stopped, or that hasn't asked for audio for 2 seconds. The app then says which device went away, notifies from the tray, and retries every 3 seconds for the first minute, then every 15 seconds. It was checked on a real stream; a physical unplug test is on the testers' checklist (what is still open).
September 10, then September 19-20
Problem. Back to back, OpenShaker felt stronger even when the recordings measured the same: "The volume on our version is still higher".
What we found.
- The first time: HaptiConnect's recordings have a perfectly silent right channel. OpenShaker played both channels, which probably add up in the ButtKicker PRO. Our recorder compared one channel, so the numbers looked equal while the seat felt the difference. The default became "Left", and the maintainer said it "feels about right now".
- The second time, found by the parity audit eight days later (September 19): "Left" was made as a single-channel stream, and Windows copies that to both channels, so up to 6 dB too much remained. A check played a test tone too quiet to feel (-60 dBFS at 50 Hz) and recorded it: the old path gave identical left and right channels, the fixed one a silent right channel.
- A side effect: everything felt in Trackmania on September 18 had been about 6 dB too strong (Trackmania gaps).
Fix. The stream now opens as wide as the device and writes silence to every unused channel. Confirmed on the device. The README warns that "Both" may feel stronger on a ButtKicker PRO.
Found September 19, in the app September 20
Problem. Found by measuring, not by feel. In the clean recordings, HaptiConnect's output stays at or below 0.985 of full scale (the one brief overshoot, to 0.991, is in a replay that held two copies of its output), and in BeamNG its engine visibly dips under every gear-change thump. OpenShaker instead clipped loud peaks, which bends the sound and never dips.
Fix.
- A look-ahead limiter like HaptiConnect's: a loud burst turns the whole mix down briefly. It looks 1.3 ms ahead and lets the volume back up along a straight ramp: from half volume to full takes about 0.6 seconds.
- The Demo got its own, lower limiter. On September 27 the test tone was found 4.2 dB weak, but only in Demo, because it passed through that limiter; it now plays after it.
- The same review found that one bad number (NaN) from a single effect could silence the whole mix. Each effect's output is now cleaned before mixing.
September 12, then September 20
Problem. Found by a random-data test, not by a user: 300 rounds of random bytes at every packet size crashed the BeamNG reader on a garbled gear value.
Fix. The readers check their input, and a bad packet is counted and skipped. Broken numbers are removed per effect and from the final mix, so garbage can't become a full-scale pop. A bug in one effect can't stop the audio stream, and ACE's memory reads are bounds-checked. Everything listens on this PC only by default (Listening only to this PC).
September 12 to 27
Problem. Without a game, the Demo played near full volume. Once the profiles matched HaptiConnect's real loudness, the Demo on the BeamNG profile peaked at almost full scale.
Fix. First a fixed, lower Demo level, with a test. Then per-profile scaling: every profile's Demo peaks at -7 dBFS at most, with a Demo limiter as a backstop.
HOW_IT_WAS_TUNED.md tells this part properly, with spectrograms and numbers. The entries here are the problems we met on the way.
Log a real drive. Replay the identical data into HaptiConnect with the game at its menu, and record its output digitally. Render OpenShaker from the same data. Line the two up, compare them, fit, and check on drives left out of the fitting. Much of this was the maintainer's idea: "record the output from the ButtKicker application and reference that with the signal".
September 10
Problem. Before any real laps, we needed to know what each HaptiConnect effect does on its own.
What we found. Claude's analogy for why test signals came first: you "measure a speaker with a sine sweep rather than by playing a song". The calibrator:
- set HaptiConnect's own game profile to one effect at full strength (after saving a backup of the maintainer's original);
- restarted HaptiConnect through Windows Explorer and fed it synthetic packets;
- recorded HaptiConnect's output, plus the telemetry HaptiConnect itself forwards, so each recording shows exactly what HaptiConnect received;
- restored the original profile from the backup.
The maintainer felt the tests from the chair, with no game running: "Are you running BeamNG in the background?" And "I'm not sure which I felt since I wasn't shown which setting was being tested".
September 10
Problem. Many of the test signals made no vibration: "Some of the tests I haven't felt anything. I'm in the chair". Seeing synthetic data, the maintainer objected: "Shouldn't we train off actual game output not your simulated output".
What we found.
- Silence was a finding, not a fault: a full-scale gear-shift test proved the chain worked.
- Some silences came from HaptiConnect's own quirks (the Motorsport plugin), and probably some from test values unlike what real games send (Kerbs you couldn't feel).
- HaptiConnect looks at the data only about 12 times a second, so events shorter than about 170 ms are hit or miss.
Fix. Real laps, replayed through both apps, became the reference.
September 10
Problem. Fed genuine Motorsport packets, HaptiConnect's gear shift, wheel lock and shift light stayed silent.
What we found. Fed the same data in the Horizon layout, its gear shift fired on every change and wheel lock responded. The plugin reads gears and pedals where a Horizon packet has them (byte 244, not 232). By inference, a Motorsport player who follows HaptiConnect's setup guide, or who sends the game's own format, never gets those effects. We didn't test that live.
Fix. For measuring only, Motorsport drives are converted to the Horizon layout before they are replayed. OpenShaker reads both layouts correctly.
September 10
Problem. The replay was meant to let the maintainer feel HaptiConnect's version of their lap: "Should I be feeling the playback? Because I don't".
What we found. HaptiConnect's output was silent, stuck on frame 0 of 25,480. The new replay tool had crashed and never sent a frame.
Fix. The tool was fixed. The first real comparison then showed OpenShaker about 11 dB louder, and "muddy".
September 10
Problem. Back to back, HaptiConnect felt better: "I feel like the HaptiConnect version felt better, more refined and less muddy".
What we found. The maintainer diagnosed it first: "our model definitely has more noise while driving in a straight line", masking the suspension feel, and asked us to record HaptiConnect's output and compare. The measurements agreed: version 1 was too loud and too busy. Its constant idle rumble, slip tone and suspension pulses fired on ordinary jitter in the data. And HaptiConnect's strength setting of 0.5 is far less than half: it is at least 15.8 dB below full strength.
Fix. Every effect was set to HaptiConnect's measured level, suspension pulses fire only on real bumps, and the kerb buzz went lower. After the next round the maintainer still noticed a difference: "I felt the rumble strips more and feelings felt sharper on HaptiConnect", which led to the kerb fixes.
September 10
Problem. The quieter version still felt strong: "the feedback felt the same, a little powerful".
What we found. Claude's reply: "Your impression is correct, and now explained". Default strengths saved in the settings file were overriding the calibrated ones.
Fix. Saved settings now override only what the user changed, and a line in the startup log prints the strengths actually in use.
- It evaluates the game data only about 12 times a second.
- It plays on the left channel only (more).
- Its strength slider isn't linear.
- It ends in a limiter that turns the whole mix down under loud bursts (more).
The numbers are in CALIBRATION.md. OpenShaker copies whatever affects the feel, but runs its effects every 10 ms.
Some wrong turns came from the measuring itself:
- A "45 Hz tone" turned out to be spill-over from HaptiConnect's 41 Hz road rumble in a short analysis window. Longer windows separate the two.
- The comparison tool's envelope correlation read 0.037 on BeamNG's flat rumble, against 0.535 with a 0.25-second window.
- An early rescaling of the BeamNG profile silently skipped the engine, which had no explicit strength setting.
- Old tables had HaptiConnect's engine tone stuck near 57 Hz at high rpm. That was an artifact of the analysis: its engine tone keeps following the rpm.
September 10, then September 18
Problem. A fair Horizon comparison needs HaptiConnect's own Horizon 5 plugin, and Horizon 5 had been uninstalled: "I can reinstall FH5 if we need to". Then: "Let's try Forza 5 through plugin vs HaptiConnect".
What we found.
- With Horizon 5 reinstalled, the Horizon references come from HaptiConnect's native Horizon 5 plugin.
- For Horizon 6 (on September 18), a lap was logged, then Horizon 5 was opened to its menu so HaptiConnect's Horizon 5 plugin would listen, and the Horizon 6 lap was replayed into it.
- No data arrived until Horizon 6 was pointed at OpenShaker's port. Then OpenShaker recognised Horizon 6 and switched preset by itself.
- HaptiConnect renders Horizon 5 and Horizon 6 laps differently at the same inputs.
Fix. One Horizon profile plays both games for now (per-game gaps).
September 18
Problem. ACE had been compared against HaptiConnect's Motorsport plugin as a stand-in, because its own ACE plugin never gets live data (Assetto Corsa EVO went silent). The maintainer's idea: "feed the ACE information we recorded into the ACC part of the HaptiConnect plugin".
What we found.
- Claude built a replayer that writes a recorded ACE drive under the old memory names, with ACE at its menu. HaptiConnect's real ACE plugin played it. That proved the diagnosis: only the names and the layout were stale.
- A surprise: the replay most likely also woke HaptiConnect's Assetto Corsa Competizione plugin (it saw ACC's version string), so the recordings probably hold two copies of every effect.
Fix. HaptiConnect's own ACE plugin became the ACE reference. The doubled recordings are scored at one copy's level, and a replay option can wake only one plugin next time (Assetto Corsa EVO). None of this ships in the app.
September 18
Problem. The first reference recordings were replayed from logged values, which lost detail: surface values clipped to 1.0, neutral stored oddly, and a fixed cylinder count and tyre temperatures.
Fix. Forza's raw packets are now logged and replayed as they were received. The old laps were recorded again as a new baseline, and new Motorsport laps keep the surface value on kerbs at its real size (around 1.8, where the old laps were capped at 1.0).
Problem. A HaptiConnect recording is only as good as the HaptiConnect that made it, and the old renderings can't be recreated.
Fix. The replay tool refuses to write into an existing HaptiConnect recording folder.
Problem. OpenShaker's old gear-shift sound picked a slightly random pitch each time (HaptiConnect's follows the rpm), so two renders of the same drive never matched exactly. That made comparisons noisy, and it made the Demo test flaky.
Fix. Renders take a seed, so the same drive renders the same way every time.
September 22
Problem. Some HaptiConnect recordings contain stretches of perfect silence while the car is driving: 12-24 % of the driving time on several Motorsport references, and 71 % on one BeamNG drive.
What we found. The cause is unknown: it could be HaptiConnect or the recording. Counting those stretches had made OpenShaker look up to 0.6 dB louder than it was.
Fix. Those moments are left out of every comparison, and their share is reported.
September 11 to 20
Problem. After a BeamNG drive the maintainer reported heavy shaking at idle: "done driving, check the idle intensity, it shakes a lot".
What we found. OpenShaker was faithful to HaptiConnect, within about 1 dB. HaptiConnect's own BeamNG preset is a near-constant, near-full-scale rumble, about 12 dB (four times) stronger than its Forza preset at the strengths the maintainer used (1 for BeamNG, 0.5 for Forza).
Fix, in three turns.
- The BeamNG profile was scaled down to the Forza level.
- The maintainer first asked for a rollback, then corrected it: "I made a mistake, I didn't really want us to roll back". The scaled level stayed, built into the one BeamNG profile.
- With the exact-parity decision, the scaling was undone. The maintainer was warned that BeamNG would get about four times stronger, and approved it.
The README now says to set the amplifier with the strongest game.
September 18 to 20
- The decision. "why wouldn't we be the exact same loudness?", and then: "I want to mimic the feel of the output from the HaptiConnect driver".
- "apply it". The second Motorsport refit improved the scores but missed two strict acceptance rules by hairlines. The maintainer approved it, and both misses were written down.
- "yes to all". Four exact-parity profiles, plus a list of code fixes, biggest first: the left-channel bug, the limiter, road rumble that follows the real suspension, braking and cornering combined, missed crashes, and a shift light that stays silent in reverse. All applied, with backups.
- The results are in HOW_IT_WAS_TUNED.md. For the write-up, the maintainer asked: "Create new spectrographs and include information about how we refined the algorithm". An independent fact-check of that write-up caught its wrong claims before it was published.
September 12
Problem. The maintainer decided to go public: "I'd like to make this app public on GitHub I think". What would the public prefer?
Fix. Claude's advice became the release plan: a normal Windows installer; nothing installed into games; no HaptiConnect needed; any bass shaker (with a choice of device and channel); ask before starting with Windows; and get along with SimHub.
September 12
- "I hate complicated GitHubs."
- "Make sure to list the hardware we did test it on."
- "I am okay with admitting these applications are created using Claude."
Fix. A one-click Setup.exe, a short README with a table of tested hardware, and a "Should also work" list for other shakers.
September 12
Problem. "I'm thinking OpenShaker or OpenKicker, check if there are projects like that already".
What we found. ButtKicker is The Guitammer Company's trademark, and "OpenKicker" sits close to the KICKER car-audio brand.
Fix. The maintainer chose OpenShaker and the MIT license. The code and the profiles got neutral names, and the copyright uses the maintainer's public handle, not their name.
September 12
Problem. Six tiny clips recorded from HaptiConnect were part of the profiles: gear shifts and impacts for Forza and BeamNG, a bump and one kerb cycle, about 0.6 seconds of audio in all.
Fix. The maintainer chose to replace them. They are now generated in code from their measured pitch, length and shape, and the recordings stay private (Generated, not copied).
September 12
Problem. An app that works on one PC may depend on something only that PC has.
Fix. The files that ship (72 of them) were copied to a clean folder with a fresh Python environment. All 157 tests passed there, and a first run with no settings file worked. Settings and logs moved to a per-user settings folder, with a one-time move of the old settings.
September 12 to 27
- September 12: asked how to label an untested first release, the maintainer chose the answer "Wait, I'll test first".
- September 18: "We don't have to push to GitHub yet, I want to make more changes."
- September 27: "I want to do OpenShaker first, I am happy with that", and the choice to publish once the checks pass.
- The release steps: freeze the code, audit it, build from a clean copy of the public code, test the install, publish.
September 18
Problem. "Alert the user of the dependencies, most players have Openplanet".
Fix. The README spells out Openplanet and Data Sender. Forza Horizon 6 was listed as "should also work" until the maintainer drove it; it now has a compared lap.
September 12 and 27
Problem. "I want it to just be a double click away from being set up".
Fix.
- One screen with two ticked boxes (Start with Windows, desktop shortcut) and Install.
- A per-user install with no administrator prompt, which then starts the app.
- Silent updates start the tray app again afterwards ("yes, relaunch the tray app after silent updates").
- The uninstaller asks about the settings.
- Build safety: the license must be there, the packages are pinned, and the build comes from a clean copy.
- One slip: the build tool emptied its output folder and deleted an old test installer (When the AI got it wrong).
The DEVELOPMENT notes explain the build.
September 27
Problem. "can we verify there is no personal information in this code"
What we found, and removed: private work notes and settings in the project folder, names based on trademarks, the recorded HaptiConnect clips, absolute paths in profile notes, and the wrong-company attribution.
Fix. A release test fails on any user-folder path or real-looking e-mail address. The final scan checked 234 real identifiers from the PC (serial numbers, device, network and account IDs) across all 97 publishable files: zero hits. The installer was built in a neutral folder, then unpacked and scanned. Pictures carry no text metadata, and commits use a private address.
Fix. Missing third-party license files were added, and one license that Python's docs lack was copied from its source. OpenSSL is left out. Only the PortAudio build without ASIO ships (the ASIO builds carry another company's SDK). pystray ships as replaceable source files, as its license asks. A disclaimer says the project isn't affiliated with the makers of the hardware, the software or the games.
September 12 and 27
Problem. "Be transparent about it being AI but I will take feedback and can fix bugs."
Fix.
- The README section Built with AI, open to feedback.
- Issue forms for bugs, hardware and feel reports, and ideas.
- A tray item that only opens the issue page: nothing is sent automatically, and the README warns that logs can contain your Windows user name.
- The installer and the README credit the maintainer and Claude together, and the license stays in the maintainer's name. Claude is described as an AI model made by Anthropic, and nothing suggests that Anthropic publishes or endorses the app.
<- Part 2: how we worked | Contents | Part 4: what is still open ->
OpenShaker by baddo & Claude - Download - Report a problem or share how it feels - Source - MIT License
1. Getting started
2. Games
3. Effects
4. Using OpenShaker
- The main window
- The tray menu
- Presets and strengths
- Advanced settings
- Start with Windows, updates and uninstalling
5. Your hardware
6. Help
7. Behind the scenes