Description
Bevy version
0.15.3 and 0.16.0-rc.5
Relevant system information
native:
2025-04-15T20:34:25.405810Z INFO bevy_diagnostic::system_information_diagnostics_plugin::internal: SystemInfo { os: "Linux 41 Fedora Linux", kernel: "6.13.9-200.fc41.x86_64", cpu: "AMD Ryzen 9 7950X 16-Core Processor", core_count: "16", memory: "30.4 GiB" }
2025-04-15T20:34:25.466728Z INFO bevy_render::renderer: AdapterInfo { name: "AMD Radeon RX 7900 XTX (RADV NAVI31)", vendor: 4098, device: 29772, device_type: DiscreteGpu, driver: "radv", driver_info: "Mesa 25.0.2", backend: Vulkan }
firefox:
INFO /home/hhh/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_render-0.15.3/src/renderer/mod.rs:199 AdapterInfo { name: "Radeon R9 200 Series, or similar", vendor: 4098, device: 0, device_type: Other, driver: "", driver_info: "WebGL 2.0", backend: Gl }
chromium:
INFO /home/hhh/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/bevy_render-0.16.0-rc.5/src/renderer/mod.rs:200 AdapterInfo { name: "ANGLE (AMD, AMD Radeon RX 7900 XTX (radeonsi navi31 LLVM 19.1.7), OpenGL 4.6)", vendor: 4098, device: 0, device_type: Other, driver: "", driver_info: "WebGL 2.0 (OpenGL ES 3.0 Chromium)", backend: Gl }
Firefox: 137.0 (64-bit)
Chromium: Version 135.0.7049.84 (Official Build) (64-bit)
What you did
Either execute this Bevy playground snippet directly or write this code:
main.rs
use std::time::Duration;
use bevy::{
input::{common_conditions::input_just_pressed, mouse::AccumulatedMouseMotion},
prelude::*,
window::CursorGrabMode,
};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(
Update,
(
count_motion,
capture_cursor.run_if(input_just_pressed(MouseButton::Left)),
),
)
.run();
}
fn count_motion(
time: Res<Time>,
accumulated_mouse_motion: Res<AccumulatedMouseMotion>,
mut motion_per_update: Local<Vec<f32>>,
mut timer: Local<Option<Timer>>,
) {
let timer = timer.get_or_insert(Timer::new(Duration::from_secs(1), TimerMode::Repeating));
let delta = accumulated_mouse_motion.delta;
motion_per_update.push(delta.length());
if timer.tick(time.delta()).finished() {
let total_move_distance = motion_per_update.iter().sum::<f32>();
let max_motion = motion_per_update
.iter()
.copied()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap_or(0.0);
let count = motion_per_update.len();
let average = total_move_distance / count as f32;
let median = median(&motion_per_update);
let zero_count = motion_per_update.iter().filter(|&x| *x == 0.0).count();
info!(
"total move distance this second: {total_move_distance} (n: {count}, n zeros: {zero_count}, max: {max_motion}, average: {average}, median: {median})"
);
motion_per_update.clear();
}
}
fn median(values: &[f32]) -> f32 {
let mut sorted = values.to_vec();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
sorted[sorted.len() / 2]
}
fn capture_cursor(mut window: Single<&mut Window>) {
window.cursor_options.visible = false;
window.cursor_options.grab_mode = CursorGrabMode::Locked;
}
Now run it once using bevy run
and once using bevy run web
. On web, open the console to see the output.
What went wrong
On Firefox:
Move the mouse around the window in web before you click into the window. Notice the output.
Now, click into the center of the window to lock the cursor. Continue moving the mouse at the same pace. Notice that the output drastically went down.
On native, you will not be able to recreate this behavior.
For me, the bug was not present on Chromium, but other users report getting the issue only on Chrome.
Additional information
Using an extra scaling factor on Wasm is not enough, as slow mouse movements get reported as absolute zero.
This might also very well be a winit bug, I didn't investigate it independently of Bevy.
This is the output for slow mouse movement:
The cutoff to the locked state should be obvious, as the numbers go down drastically.
Note that the message saying that the mouse moved a distance of zero is collapsed as it is repeated 6(!) times.
This is the output for fast mouse movement:
Note that the amount of zeros does not go up, but the maximal distance and average distance per second are drastically lower.