context-attachment-manager
Claude Desktop용 플러그인, 다음 메시지에 사용할 과거 이미지 선택 가능
저장소 정보는 아직 수집하지 않았습니다.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
Chrome extension for surfd.net: a glanceable HUD with per-ramp speed-loss feedback, start stats, PB/WR splits, crouch-jump and nullbinds.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다.
README 전문 보기 ↗The compact HUD you surf with, just after a checkpoint. Replays and spectating show everything, and each part can be shown or hidden on its own.
A browser extension for surfd.net that shows where you lose speed, on each ramp and in the air, so you can find your mistakes and see what an optimal run does differently. It also adds start stats, PB and WR checkpoint splits, run history, replay tools with a cinematic camera and other runs alongside, private rooms, a crouch-jump bind and nullbinds.
Everything on the HUD can be configured or turned off. The code is plain JavaScript with no dependencies, so it's easy to change or extend yourself, or with your own coding agent. It preserves movement rules, maps and your runs: feedback watches the game's own movement code and reports what happened.
Download page: eduardr02.github.io/surfd-qol
You need a desktop Chromium browser: Chrome 118 or newer, Edge, Brave or Opera (Firefox isn't supported). The extension isn't on the Chrome Web Store, so you load it yourself:
chrome://extensions, edge://extensions, brave://extensions or opera://extensions) and switch on Developer mode.manifest.json).Updating: when a new version is out, a link appears above QoL in the game menu. Download the new zip, replace the folder's contents, then click the reload icon on the extension on your browser's extensions page and reload Surfd. Your settings stay.
Your browser shows this bar because the extension uses the debugger permission to patch the game script as it loads. That is the only way to reach the game's private physics objects. It only attaches to surfd.net's game page and touches assets/game-*.js and the game's checkpoint-speed, ghost and replay-index worker scripts.
You can close the bar with × or Cancel. Everything already loaded keeps working, including the binds, until you reload the page. The toolbar badge shows ON when the extension is active on the page and ! if setup failed or the bar was closed before setup finished. Click the toolbar button to reconnect; that reloads the tab, so do it between runs. The bar comes back after each reload.
Speed sits big on the centre line of the screen, with a bar for your last ramp under it. Size, height, opacity and all colors are adjustable, and the HUD uses its own bundled font (Instrument Sans), so nothing is fetched from the web. It hides in menus, loading screens and dialogs, and stays up for replays and spectating.
Two layouts. While you surf, the HUD stays compact: the speed and the ramp bar with two labelled values under it, and no timer (turn on Timer under While surfing if you want it). Turn off Value labels under Display for numbers alone. For a few seconds after the start and each checkpoint, your start stats or checkpoint deltas show above the speed. Run labels like PRACTICE or INVALID still show. Replays and spectating show the full HUD: the timer and strafe sync, each checkpoint's split, and every ramp value written out. Set While surfing to Full under Display to keep everything on while you run. Zen keeps just the speed and the start stats.
The ramp bar keeps one width, centred under the speed. Your last flight (AIR) and the ramp you landed on share it in the order they happened: the flight, then the touch and the rest of the ramp, each part in the color of its grade. Choosing Down + Up splits the ramp into downhill and uphill portions. The tick marks where the ramp would have ended had it cost what the reference ramp did, so whatever lies right of it is what you lost beyond the reference. Beat the reference and your parts end early, with the tick at the bar's end. With fixed grading there's no reference, so there's no tick. While surfing, the two values under the bar are the flight's loss and what the ramp has cost you so far: its touch right after you land, then the whole ramp once you leave it. Turn off Ramp bar under Display to keep just the values.
The values, all in u/s. Each one stays until the next ramp or flight replaces it, so you can glance at it in the air instead of mid-ramp.
| Value | What it measures |
|---|---|
| TOUCH | Speed clipped off when you touch a ramp. Pure collision loss: height conversion doesn't count. Quick re-touches during the landing count as part of the same touch. It's also the first part of DOWN, so DOWN and SURF include it. |
| DOWN | What the ramp cost you on the way down, from the moment you touched it (so it includes the touch) to the lowest point you reached. |
| UP | What it cost you from the lowest point to where you left the ramp: the turn up and the exit. |
| AIR | Your last flight between ramps: speed lost to air braking plus gain missed by imperfect strafes. You can show either part on its own. |
| SURF | The whole ramp: DOWN + UP, touch included. It's the value under the bar while you surf. |
Down and Up are measured against perfect gravity-only conversion. If you land at 2500 u/s and leave lower or higher, the height change is converted back to speed before comparing, so climbing a ramp is never counted as a loss. Down + Up equals the whole-ramp loss exactly, touch included. Net Surf is the default; choose Down + Up under QoL → Feedback → Show as for the split.
Don't chase zero. Ramp surfing always bleeds some speed; on Cannonball's second ramp the KSF record still loses 34 u/s going down and 16 coming up. That's why the useful comparison is against a reference run, which is the default.
Turn any of them off and the bar and the values leave it out.
Touch and the ramp values compare against the same ramp in a reference run, matched by ramp, checkpoint and visit:
The reference can be your PB replay or the WR. "WR" means the KSF record route (the site's "Watch the record route") by default, or Surfd's leaderboard #1. You can also switch any value to fixed cutoffs. Air always uses fixed cutoffs, since there's no meaningful per-flight reference.
A worse number is a measurement, not proof of a mistake. A different line or entry speed changes it too.
For 1.8 seconds after you leave the start zone you see your exit speed (350 is the airborne cap, 325 grounded) and your height above the start platform (lower is better; crouching doesn't fake it), where the checkpoint deltas go. While surfing, your strafe sync sits between them. Grade them against fixed targets, your PB or the WR.
At each checkpoint you get time and speed deltas against your PB, the WR, or both, for a few seconds; the full HUD also shows the split's own time and speed above them. The reference comes from the full replay, never per-checkpoint bests.
The full HUD, as replays and spectating show it, just after a checkpoint.
Both work inside the game's own input sampling, so the commands it records are exactly what you'd get from a keyboard that did this in hardware. Your binds on disk aren't changed.
Not sure how fast to turn? Stand still in the start zone, open QoL → Controls, enter a speed in u/s, pick Ground (the prestrafe) or Air, and press Start. Your view turns the way a good strafe would: alternating strokes at the best turn rate for your speed, while the speed grows as perfect strafing would grow it. The HUD shows which of A or D you'd hold, the speed and the turn rate. Any key, mouse button or mouse movement stops it and puts your view back. It only moves the camera: no keys are pressed and you don't move.
Runs, next to QoL in the menu, opens on the map you're on; pick another map from the title.
Bests count clean recorded runs, including ones you reset. Practice and invalid runs don't count. Short resets don't fill up your recent history, but your latest attempt is always available.
The average start leaves out clipped starts, where you landed back in the start zone after your jump, so the game blocked the next hop and you left on the ground. Averages from earlier versions included them, so each map's average starts over at its next clean start.
The last 10 count measurements of each ramp, not your last 10 runs. Averages start collecting when you update; old recordings aren't re-analysed. Recent best uses your existing replay allowance, not extra storage.
▶ in Runs plays a recording with the full HUD, including the prestrafe. Surfd's own player handles playback, seeking and indexing for your recordings, leaderboard runs, the record route and server replays.
The custom replay bar is on by default, with Saveloc, Runs and saved-location marks. Ramp touches are marked on the timeline, coloured like the HUD: your recorded runs have them from the moment you surfed them, and any other replay (your PB, the WR, the leaderboard) gets them a moment after the game has indexed it. Each replay is analysed once and remembered. Playback shortcuts stay the game's own.
Savelocs: click Saveloc or use your usual Save position bind to bookmark a moment. Your saved moments become practice savelocs when you leave the replay, so you can load them and try the section yourself. Loading one keeps the start position you set.
On recognised game builds, Restart current stage also works after visiting a practice station or loading a practice saveloc, even without the preceding checkpoint splits. It uses the native stage spawn and keeps the session in practice; replay simulation and ranked-run restart rules stay unchanged.
Prefer Surfd's bar? Turn off QoL → History → Custom replay bar. Both bars drive the same native player and show the same touch marks and savelocs. You can switch while watching.
After you seek to a part you haven't watched yet, feedback can stay grey until the next ramp or flight: the game restores the movement there, but the extension hasn't analysed what came before, so it leaves that measurement out.
While you watch any replay, the view menu on the replay bar picks how you watch it: POV (the player's own first-person view, where every replay starts), Steady or Cinematic. Both cameras work for a single run too. Add run puts other runs in the same replay as ghosts: your PB, your recent best, the site WR, the KSF WR, or any leaderboard run (search by name or rank). Adding a run switches to the camera you used last (Cinematic the first time). The replay bar, seeking and stage jumps work as usual.
The Fiellu record with four more runs added, in Cinematic. Each chip shows that run's gap to the one you're watching.
First use re-simulates the replay in the background. The camera can start once a recorded window around the playhead is ready, without waiting for the end of the map; paths fill in as recording advances. If the completed plan arrives during the initial POV flight, it preserves the camera's incoming velocity and remaining flight time rather than restarting the move. A seek outside the prepared window temporarily uses native POV rather than a stale camera position. Adding a run starts the camera independently of its download and processing. Nothing runs before you request it. Reopening the same replay and loaded map reuses its complete track and solo plans; unfinished windows are not cached. Closing still frees ghosts and GPU resources. Where parallel shader compilation is supported, shaders prepare alongside camera planning while POV keeps rendering; other clients keep the existing first-draw path.
While already in Steady or Cinematic, composition changes blend an offset around the moving destination. Adding or removing a run, changing focus or switching styles keeps following the replay rather than stopping at the previous camera position. Another reframe during the flight preserves its current motion, including when an added run becomes ready during the initial flight out of POV.
Route matching and camera preparation share idle time in bounded batches, so adding a run does not chain those calculations into one playback-blocking task. The matching, checkpoint anchors and planned shots are unchanged.
Both cameras check the finished plan and the final interpolated or blended view against opaque map geometry. Cramped shots ease into a clear chase along the travelled route where possible; moving closer than the usual shot distance is the fallback when there is no room. Clear shots retain their planned framing.
Solo and focused planning reuse unchanged framing calculations across correction passes. Route-fallback searches yield according to actual search work, including sparse obstructions and inner candidate loops, so one difficult camera sample cannot skip cancellation checks.
Cached CPU results stay in the tab's memory, not on disk. They use weak replay keys and weak map/world identity references, so they do not keep an unloaded map alive. Reloading the page clears the cache.
There's no limit on how many runs you add, but each costs some performance, so remove runs if playback gets slow.
Under QoL → History you can turn recording off, choose how many recent runs to keep per map and set the minimum reset length. Each run includes its replay. Your best recorded finish, best splits and ramp bests stay even after older attempts are removed.
Everything is stored in this browser. The same page shows storage use and has Clear all.
Replays get full feedback, plus a Hull: crouched / standing line in the game's key display, so you can see when the player is surfing crouched after letting go of crouch. Spectating shows speed, time and checkpoint progress only, since network updates don't carry enough data for exact feedback. Energy tint is also unavailable while spectating: interpolated network poses can look like gains or losses even during a physically neutral arc. The setting still applies to your own runs and locally simulated replays.
Surf with friends in a room of your own: no time limit, no map votes, and the host picks the map. Everyone in the room needs Surfd QoL.
The extension doesn't change physics, collision, timing or replays, and it doesn't submit anything. The binds change which keys count as pressed, just like keyboard software would; the result is recorded in your replay as normal input. Whether the leaderboard accepts runs made with binds is up to Surfd; see Surfd's terms.
You only need Bun to work on the source; no other dependencies.
bun run build # writes the unpacked extension to dist/
bun test
Tests run offline against fixtures: none of them load the site, the game's code or the signalling Worker. (bun signal/check-live.mjs checks a deployed Worker, by hand.)
Load dist/ with Load unpacked while developing. To publish a release, bump version in extension/manifest.json, commit, then push a matching tag (git tag v1.0.1 && git push origin v1.0.1). The release workflow tests, builds and attaches surfd-qol.zip to a GitHub release, which the download links always point at. The download page lives in docs/ and is served by GitHub Pages; dev/ has a harness for rendering HUD states (see dev/README.md).
On recognised builds, the loader unrolls the native collision-box helper in the main module and those three physics workers. It keeps the same inclusive comparisons and axis order, including NaN rejection. The replay-index worker can reuse the loaded map's collision geometry and existing BVH: packing yields between short slices, then transfers newly allocated Float64 geometry/bounds, brush-plane and tree-index buffers without detaching live map data. Render-only brush fields and import metadata stay out of the packet. The worker queries the packed tree directly and materialises contacted brushes and triangles lazily; native candidate order, collision flags and movement rules stay unchanged. Incompatible map ownership or unsupported layouts retain native geometry loading and the optimised collision builder. The cinematic recorder captures new airborne contacts on its private shadow movement instance instead of installing general-purpose prototype observers for each recording batch.
The index worker is created on the first replay watch, not during map loading. Later watches under the same renderer and map/replay revision reuse its collision world without packing or sending it again, including separately parsed archived maps; replay simulation state is fresh each time. Renderer disposal, revision changes and worker failures release the retained world. The worker has no idle polling or timers, and does not cache completed indexes. Cancellation yields between active indexing batches; a dispatched worker whose world is not ready is terminated so an obsolete geometry fetch cannot hold up the next watch. Generation-tagged results cannot overwrite a newer replay. An incompatible or failed worker returns control to the native indexer's fallback.
Recognised collision-tree constructors select median partitions instead of repeatedly sorting and copying every subtree. Flat temporary bounds/centre arrays keep construction work predictable; longest-axis selection, stable ID ties, leaf order, node bounds and native query methods are preserved. Unsupported constructor layouts or non-finite inputs keep the native constructor. This applies to initial map loading and physics-worker fallback builds; replay workers can still reuse the already-built tree.
The extension's ramp-surface index uses median partitions and shared leaf ranges rather than recursively sorting and copying faces. Recognised map preparation builds the cached index before play, removing the first-tick construction stall that could trigger the native frame-stall pause immediately after entry.
The recognised BSP loader pipelines pack reads, fragment assembly and bounded image decoding. Collision, all mesh data and complete skyboxes are ready before entry; absent or incomplete skyboxes retain the native fog fallback. A native visibility traversal selects the other textures required for the initial view. Off-screen textures finish in the background and update their existing texture objects without reducing resolution. Background failures invalidate the map through the native load-error UI; cancellation and renderer disposal stop outstanding work and release decoded images. Mesh loading remains upfront: the packed format lacks per-mesh bounds, and shared world batches can span most of a map.
The initial fully textured view does not wait for all-map GPU warm-up. Once remaining resources finish, texture uploads and small geometry batches run in idle time. Visibility and scissor state are restored before yielding, touched instance batches are invalidated for the next native frame, and disposed maps stop scheduling work.
Where supported, WebCodecs decodes packed image bytes directly, then converts the decoded frame to ImageBitmap without a per-image Blob read. Frames and decoders close immediately after conversion; unsupported codecs or conversion fall back to Blob/ImageBitmap and then the native image loader. ImageBitmap preserves the existing upload, orientation and alpha handling. Bitmaps close when loading fails, is cancelled, or their renderer is disposed. This adds no persistent image cache and does not reduce texture quality.
Recognised skybox resampling keeps native face dimensions, affine transforms, linear-light blending and alpha rounding. Identity faces at the output size copy pixels directly; interpolation uses cached colour-transfer lookups, with the exact native conversion at ambiguous rounding boundaries. This avoids the native per-channel closure and repeated power-function work when sky faces have different sizes. Same-size identity skyboxes retain their no-resampling path; unknown native layouts stay unchanged.
Source layout:
extension/: manifest, the background loader (debugger attach and script patching), instrument.js and optional native hooks for rooms, savelocs, live practice stage resets, collision preparation, image decoding, sky resampling and progressive map GPU preparation.src/metrics.js: per-tick movement analysis (Touch, Down/Up/Surf, Air, live feedback).src/observer.js: wraps the game's movement methods without changing their results.src/hud-model.js: decides what the HUD shows; src/hud.js renders it and the settings panel.src/strafe-preview.js, src/strafe-preview-ui.js: the strafe preview and its indicator.src/energy.js: the speed tint's energy trend.src/references.js, src/run-references.js, src/replay-store.js: PB/WR replay download, native re-simulation and ramp matching.src/run-history.js, src/runs-panel.js, src/ramp-reference.js: recent runs and per-map bests, the Runs panel, and PB/WR ramp values to compare against.src/replay-archive.js, src/replay-trim.js: saved replays (compressed, in IndexedDB), trimmed to a few seconds before the start.src/replay-control.js, src/replay-frames.js, src/replay-bar.js, src/native-replay-ui.js: native-player integration, bounded analysis checkpoints, replay savelocs and the native/custom replay bars.src/cinema.js and src/cinema-*.js: cinematic replays: runs re-simulated as tracks, matching them in time or place, the planned camera, the ghosts and trails, and their controls.src/private-rooms/, signal/: private rooms in the browser, and the Cloudflare Worker that connects their players (see signal/README.md).src/start.js, src/bindings.js, src/settings.js, src/visibility.js: start stats, binds, settings schema and HUD visibility rules.MIT. The bundled HUD font, Instrument Sans, is under the SIL Open Font License 1.1.