Looking at the performance of HTML5 exported content, it looks like UE4 implements capping of maximum FPS by synchronously sleeping in the code to delay the processing of the next game frame.
Digging around, it seems that this feature is enabled if the developer ticks either of the “Smooth Frame Rate” or “Use Fixed Frame Rate” options in the Project Settings → General Settings menu.
When either of these is enabled, the C++ code will trigger calling sleeps to have the C++ code throttle until it’s time to process the next game tick:
Unfortunately in web browsers this is harmful because of two reasons:
the minor reason is that there does not exist a browser API to have the CPU sleep while conserving battery. In Emscripten, sleeping is implemented via a busy spin wait loop until the time is up. The upcoming SharedArrayBuffer specification will lift this restriction for Web Worker contexts, but not for the main thread.
the major reason is that since the sleeps are synchronous and occur on the main browser thread, this will lock up the operation of the browser as well, so e.g. page scrolling, navigation and other activities can be are adversely affected.
Measuring a UE4 exported page with either of the frame rate capping features enabled, the impacted profile can look like as follows:
where about 10% of total CPU time is eaten up by the sleep this profile.
In HTML5 exports, in order to keep the browser responsive, it is the browser’s responsibility to implement the ticking of the main loop. The tick rate is always tied to the vsync refresh rate of the monitor, so the appropriate fps capping is always enforced by the browser (to vsync, most commonly 60fps). It would be possible to choose other tick rates that are integer denominations of the main vsync rate (i.e. 60/2=30fps, 60/3=20fps, 60/4=15fps and so on) by calling the emscripten_set_main_loop_timing() function with the desired integer denomination. This would correspond to the native GL “swap interval” abstraction.
I would recommend that UE4 would issue a noticeable warning when one exports a HTML5 project with either of these features enabled, or alternatively these could even be force-disabled for HTML5 exports. Although if force-disabled, it would be good to document somewhere that these checkboxes don’t have a function for HTML5 exports.
Developers should make sure to avoid the Smooth Frame Rate and Use Fixed Frame Rate features when exporting projects to HTML5. This can improve browser responsiveness and reduce battery usage.