Skip to content

Reported mouse movement is massively slower on Wasm than on native when cursor is locked #18855

Open
@janhohenheim

Description

@janhohenheim

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:

Image
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:
Image
Note that the amount of zeros does not go up, but the maximal distance and average distance per second are drastically lower.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-InputPlayer input via keyboard, mouse, gamepad, and moreA-WindowingPlatform-agnostic interface layer to run your app inC-BugAn unexpected or incorrect behaviorO-WebSpecific to web (WASM) buildsS-BlockedThis cannot move forward until something else changes

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions