Skip to content

Commit c9995c0

Browse files
committed
Revert "Bump up the target rate to 120fps. (tldraw#6868)"
This reverts commit 2e5f9f2.
1 parent 8985a3e commit c9995c0

File tree

4 files changed

+11
-62
lines changed

4 files changed

+11
-62
lines changed

packages/sync-core/src/lib/TLSyncClient.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,6 @@ import {
3535
*/
3636
export type SubscribingFn<T> = (cb: (val: T) => void) => () => void
3737

38-
/** Network sync frame rate when in solo mode (no collaborators) @internal */
39-
const SOLO_MODE_FPS = 1
40-
41-
/** Network sync frame rate when in collaborative mode (with collaborators) @internal */
42-
const COLLABORATIVE_MODE_FPS = 30
43-
4438
/**
4539
* WebSocket close code used by the server to signal a non-recoverable sync error.
4640
* This close code indicates that the connection is being terminated due to an error
@@ -806,11 +800,6 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
806800
this.flushPendingPushRequests()
807801
}
808802

809-
/** Get the target FPS for network operations based on presence mode */
810-
private getSyncFps(): number {
811-
return this.presenceMode?.get() === 'solo' ? SOLO_MODE_FPS : COLLABORATIVE_MODE_FPS
812-
}
813-
814803
/** Send any unsent push requests to the server */
815804
private flushPendingPushRequests = fpsThrottle(() => {
816805
this.debug('flushing pending push requests', {
@@ -830,7 +819,7 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
830819
pendingPushRequest.sent = true
831820
}
832821
}
833-
}, this.getSyncFps.bind(this))
822+
})
834823

835824
/**
836825
* Applies a 'network' diff to the store this does value-based equality checking so that if the
@@ -938,5 +927,5 @@ export class TLSyncClient<R extends UnknownRecord, S extends Store<R> = Store<R>
938927
}
939928
}
940929

941-
private scheduleRebase = fpsThrottle(this.rebase, this.getSyncFps.bind(this))
930+
private scheduleRebase = fpsThrottle(this.rebase)
942931
}

packages/tldraw/src/lib/ui/components/DefaultDebugPanel.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ function FPS() {
110110
isSlow = !isSlow
111111
}
112112

113-
fpsRef.current!.innerHTML = `FPS ${fps.toString()} (max: ${maxKnownFps})`
113+
fpsRef.current!.innerHTML = `FPS ${fps.toString()}`
114114
fpsRef.current!.className =
115115
`tlui-debug-panel__fps` + (isSlow ? ` tlui-debug-panel__fps__slow` : ``)
116116

packages/utils/api-report.api.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ export function filterEntries<Key extends string, Value>(object: {
126126
export function fpsThrottle(fn: {
127127
(): void;
128128
cancel?(): void;
129-
}, getTargetFps?: () => number): {
129+
}): {
130130
(): void;
131131
cancel?(): void;
132132
};

packages/utils/src/lib/throttle.ts

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,15 @@ const isTest = () =>
55
!globalThis.__FORCE_RAF_IN_TESTS__
66

77
const fpsQueue: Array<() => void> = []
8-
const targetFps = 120
9-
// Browsers aren't precise with frame timing - this factor prevents skipping frames unnecessarily
10-
// by aiming slightly below the theoretical frame duration (e.g., ~7.5ms instead of 8.33ms for 120fps)
11-
const timingVarianceFactor = 0.9
12-
const targetTimePerFrame = Math.floor(1000 / targetFps) * timingVarianceFactor // ~7ms
8+
const targetFps = 60
9+
const targetTimePerFrame = Math.floor(1000 / targetFps) * 0.9 // ~15ms - we allow for some variance as browsers aren't that precise.
1310
let frameRaf: undefined | number
1411
let flushRaf: undefined | number
1512
let lastFlushTime = -targetTimePerFrame
1613

17-
// Track custom FPS timing per function
18-
const customFpsLastRunTime = new WeakMap<() => void, number>()
19-
// Map function to its custom FPS getter
20-
const customFpsGetters = new WeakMap<() => void, () => number>()
21-
2214
const flush = () => {
2315
const queue = fpsQueue.splice(0, fpsQueue.length)
2416
for (const fn of queue) {
25-
// If this function has custom FPS, update timestamp when executing
26-
const getTargetFps = customFpsGetters.get(fn)
27-
if (getTargetFps) {
28-
customFpsLastRunTime.set(fn, Date.now())
29-
}
3017
fn()
3118
}
3219
}
@@ -65,41 +52,30 @@ function tick(isOnNextFrame = false) {
6552
}
6653

6754
/**
68-
* Creates a throttled version of a function that executes at most once per frame.
69-
* The default target frame rate is 120fps, but can be customized per function.
55+
* Creates a throttled version of a function that executes at most once per frame (60fps).
7056
* Subsequent calls within the same frame are ignored, ensuring smooth performance
7157
* for high-frequency events like mouse movements or scroll events.
7258
*
7359
* @param fn - The function to throttle, optionally with a cancel method
74-
* @param getTargetFps - Optional function that returns the current target FPS rate for custom throttling
7560
* @returns A throttled function with an optional cancel method to remove pending calls
7661
*
7762
* @example
7863
* ```ts
79-
* // Default 120fps throttling
8064
* const updateCanvas = fpsThrottle(() => {
81-
* // This will run at most once per frame (~8.33ms)
65+
* // This will run at most once per frame (~16.67ms)
8266
* redrawCanvas()
8367
* })
8468
*
85-
* // Call as often as you want - automatically throttled to 120fps
69+
* // Call as often as you want - automatically throttled to 60fps
8670
* document.addEventListener('mousemove', updateCanvas)
8771
*
8872
* // Cancel pending calls if needed
8973
* updateCanvas.cancel?.()
90-
*
91-
* // Custom FPS throttling for less critical updates
92-
* const slowUpdate = fpsThrottle(() => {
93-
* heavyComputation()
94-
* }, () => 30) // Throttle to 30fps
9574
* ```
9675
*
9776
* @internal
9877
*/
99-
export function fpsThrottle(
100-
fn: { (): void; cancel?(): void },
101-
getTargetFps?: () => number
102-
): {
78+
export function fpsThrottle(fn: { (): void; cancel?(): void }): {
10379
(): void
10480
cancel?(): void
10581
} {
@@ -117,23 +93,7 @@ export function fpsThrottle(
11793
return fn
11894
}
11995

120-
// Store custom FPS getter if provided
121-
if (getTargetFps) {
122-
customFpsGetters.set(fn, getTargetFps)
123-
}
124-
12596
const throttledFn = () => {
126-
// Custom FPS - check timing before queuing
127-
if (getTargetFps) {
128-
const lastRun = customFpsLastRunTime.get(fn) ?? -Infinity
129-
const customTimePerFrame = Math.floor(1000 / getTargetFps()) * timingVarianceFactor
130-
const elapsed = Date.now() - lastRun
131-
132-
if (elapsed < customTimePerFrame) {
133-
// Not ready yet, don't queue
134-
return
135-
}
136-
}
13797
if (fpsQueue.includes(fn)) {
13898
return
13999
}
@@ -150,7 +110,7 @@ export function fpsThrottle(
150110
}
151111

152112
/**
153-
* Schedules a function to execute on the next animation frame, targeting 120fps.
113+
* Schedules a function to execute on the next animation frame, targeting 60fps.
154114
* If the same function is passed multiple times before the frame executes,
155115
* it will only be called once, effectively batching multiple calls.
156116
*

0 commit comments

Comments
 (0)