Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WASM] - Custom Fonts loading time #4968

Closed
GuilhE opened this issue Jun 14, 2024 · 7 comments
Closed

[WASM] - Custom Fonts loading time #4968

GuilhE opened this issue Jun 14, 2024 · 7 comments
Assignees
Labels
enhancement New feature or request web

Comments

@GuilhE
Copy link
Contributor

GuilhE commented Jun 14, 2024

Describe the bug
Depending on the platform we're running the wasm app, it is noticeable the loading time of custom fonts.

Affected platforms

  • Web (K/Wasm) - Canvas based API

Versions

  • Libraries:
    • Compose Multiplatform version: 1.6.11
    • Kotlin version: 2.0.0
    • OS architecture (x86 or arm64): arm64

To Reproduce
Steps to reproduce the behavior:

  1. Open https://guilhe.github.io/WhosNext/ on devices that are compatible with WASM
  2. Play with it

Expected behavior
Unnoticeable fonts loading time

Screenshots
Faster: OSX 14.5 (23F79) | Firefox 126.0.1:

osx.firefox.126.0.1.mov

Slow: Google Pixel 3a (971AYOZ223) Android 12 API 32 | Chrome 125.0.6422.113:

Google.Pixel.3a.971AYOZ223.Android.12.API.32.-.Chrome.125.0.6422.113.mp4

Slowest: Allwinner T8100 (T8100C045298) Android 10 API 29 | Chrome 126.0.6478.71:

Allwinner.T8100.T8100C045298.Android.10.API.29.-.Chrome.126.0.6478.71.mp4
@GuilhE GuilhE added bug Something isn't working submitted labels Jun 14, 2024
@Schahen Schahen added web and removed submitted labels Jun 14, 2024
@eymar
Copy link
Collaborator

eymar commented Jul 10, 2024

Anything that involves network requests (including fonts loading) will take some time to complete. Therefore, it can be barely noticeable if the connection is fast and the latency is low. But If the connection is not so fast, the UX is indeed affected.


What are the options to improve the situation:

  • There is an experimental Local Fonts API available only in Chrome: https://developer.mozilla.org/en-US/docs/Web/API/Local_Font_Access_API - so you might avoid the network request. Not all fonts will be available though (depends on the system).
  • Alternatively, it's worth to preload the fonts during the app initialization - before displaying the content.

Could you please point to the place in code where you currently do Fonts loading?

@eymar eymar added enhancement New feature or request and removed bug Something isn't working labels Jul 10, 2024
@GuilhE
Copy link
Contributor Author

GuilhE commented Jul 10, 2024

The tests were conducted in the same network and the fonts are local resources in the project, therefore I believe the problem remains with hardware and possible code optimisations.

I'm using composeResources so I believe the loading operations are automatic:

@eymar
Copy link
Collaborator

eymar commented Jul 10, 2024

An improvement:

internal val labelTypography: Typography
    @Composable
    get() {
        val ff = helveticaNeueFontFamily // call getter only once, so the `fetch` wan't happen every time
        return Typography(
            displayLarge = MaterialTheme.typography.displayLarge.copy(fontFamily = ff),
            displayMedium = MaterialTheme.typography.displayMedium.copy(fontFamily = ff),
            displaySmall = MaterialTheme.typography.displaySmall.copy(fontFamily = ff),
            headlineLarge = MaterialTheme.typography.headlineLarge.copy(fontFamily = ff),
            headlineMedium = MaterialTheme.typography.headlineMedium.copy(fontFamily = ff),
            headlineSmall = MaterialTheme.typography.headlineSmall.copy(fontFamily = ff),
            titleLarge = MaterialTheme.typography.titleLarge.copy(fontFamily = ff),
            titleMedium = MaterialTheme.typography.titleMedium.copy(fontFamily = ff),
            titleSmall = MaterialTheme.typography.titleSmall.copy(fontFamily = ff),
            bodyLarge = MaterialTheme.typography.bodyLarge.copy(fontFamily = ff),
            bodyMedium = MaterialTheme.typography.bodyMedium.copy(fontFamily = ff),
            bodySmall = MaterialTheme.typography.bodySmall.copy(fontFamily = ff),
            labelLarge = MaterialTheme.typography.labelLarge.copy(fontFamily = ff),
            labelMedium = MaterialTheme.typography.labelMedium.copy(fontFamily = ff),
            labelSmall = MaterialTheme.typography.labelSmall.copy(fontFamily = ff),
        )
    }

Also, give 1.7.0-dev1721 a try. It has this change: #4893 which might help a bit. (using kotlin = "2.0.20-Beta2")

Also, using rel=preload for the resources such as fonts might be beneficial: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload

@GuilhE
Copy link
Contributor Author

GuilhE commented Jul 10, 2024

Nice tips, thanks!

val ff = helveticaNeueFontFamily // call getter only once, so the `fetch` wan't happen every time

✅ - this one shows significant improvements

Also, give 1.7.0-dev1721 a try. It has this change: #4893 which might help a bit. (using kotlin = "2.0.20-Beta2")

Also, using rel=preload for the resources such as fonts might be beneficial: https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/rel/preload

Where should I apply this one? The only <link> I have is <link rel="icon" type="image/x-icon" href="icon.png">

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Who'sNext</title>
        <link rel="icon" type="image/x-icon" href="icon.png">
        <script type="application/javascript" src="skiko.js"></script>
        <script type="application/javascript" src="browserApp.js"></script>
        <meta content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=3, user-scalable=yes" name="viewport">
    </head>
    <body>
        <canvas id="WhosNext"></canvas>
    </body>
</html>

@eymar
Copy link
Collaborator

eymar commented Jul 11, 2024

This is an example from a different app:

<link rel="preload" href="./composeResources/username.composeapp.generated.resources/font/FiraMono-Regular.ttf" as="fetch" type="font/ttf" crossorigin/>

@GuilhE
Copy link
Contributor Author

GuilhE commented Jul 11, 2024

So, my final report:

Using rel="preload" and storing the fontFamiliy as a val inside the composable function improves significantly the performance. But combining it with 1.7.0-dev1721 removes any font lag.

Tested on the slowest physical device I have:

  • Allwinner T8100 (T8100C045298)
  • Android 10 API 29
  • Chrome 126.0.6478.71
  • Power save mode ON

No optimizations:

no-opt.mp4

Optimizations + 1.6.11:

1.6.11.mp4

Optimizations + 1.7.0-dev1721:

1.7.0-dev1721.mp4

@eymar I think we can close this issue and possible reopen if next Beta or RC breaks this, but I believe it would be very beneficial that this optimisations hints (especially the html one) could be documented.

@okushnikov
Copy link

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request web
Projects
None yet
Development

No branches or pull requests

4 participants