Question about Wasm embedded + JavaScriptKit

Hi all,
I am experimenting with porting a small game I made using embedded Swift for esp32c6 to Wasm embedded.

I first followed the guide Getting Started with Swift SDKs for WebAssembly and got the basic functionality working:

But then I would also like to add JavaScriptKit to the mix, and found that the examples here uses earlier versions of the toolchain, so I'm not certain if the compiler and linker flags are still relevant:

I had trouble getting this to work and was uncertain about the dependency to swift-dlmalloc which I didn't see in other tutorials, as well as the experimental features which I believe are not experimental any more.

So I discarded that version of the embedded example and instead looked at the JavaScriptKit Hello World tutorial which helped me further:

https://swiftpackageindex.com/swiftwasm/JavaScriptKit/0.37.0/tutorials/javascriptkit/hello-world

I followed the tutorial, but used the embedded SDK from the Wasm Getting Started guide on swift.org.

In order to make that work I added two of the linker flags from the embedded example:

            linkerSettings: [
                .unsafeFlags(["-Xclang-linker", "-mexec-model=reactor",
                              "-Xlinker", "--export-if-defined=__main_argc_argv",
                             ])
            ]

The first one I added since a helpful error message pointed me in that direction - and the second since my project contained top-level code, and I couldn't get a @main-annotated struct to work.

So far, so good. Now I can package my project using:
swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn -c release
(where SWIFT_SDK_ID is swift-6.2.3-RELEASE_wasm-embedded)

After adding a JSClosure, however, I now get an error in the browser when running the project:

[Error] Unhandled Promise Rejection: TypeError: this.exports.swjs_library_features is not a function. (In 'this.exports.swjs_library_features()', 'this.exports.swjs_library_features' is undefined)

In the packaged javascript code I can see the failing code, but I don't know where the swjs_library_features() definition is supposed to come from - or if the JSClosure I am adding is even supported in embedded mode.

So my question is: Can I make the JSClosure work in embedded mode, and how / where might I define the swjs_library_features?

I apologize if I have started out in the wrong place or missed something essential in one of the guides.

Here's my full Package.swift:

// swift-tools-version: 6.2
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Hello",
    dependencies: [
        .package(url: "https://github.com/swiftwasm/JavaScriptKit.git", revision: "4b327dc40d3931c2236ed43c6b47325c17c88afe"),
    ],
    targets: [
        .executableTarget(
            name: "Hello",
            dependencies: [
                .product(name: "JavaScriptKit", package: "JavaScriptKit"),
            ],
            linkerSettings: [
                .unsafeFlags(["-Xclang-linker", "-mexec-model=reactor",
                              "-Xlinker", "--export-if-defined=__main_argc_argv",
                             ])
            ]
        ),
    ]
)

Hi @Morten_Bek_Ditlevsen, thanks for reporting the issue!

I've fixed the example code in this PR: Fix `Examples/Embedded` not building with Swift 6.2.3 by MaxDesiatov · Pull Request #483 · swiftwasm/JavaScriptKit · GitHub.

The linker flags you've mentioned are not needed, same for the custom allocator and shims that outdated example code included. I've verified that the updated example package in the PR (which contains JSClosure to confirm that part also works) builds with this command:

swift package \
  --swift-sdk "$(swiftc -print-target-info | jq -r '.swiftCompilerTag')_wasm-embedded" \
  js --use-cdn
1 Like

Thank you so much, @Max_Desiatov !
I look forward to trying it out! :blush:

1 Like

Thanks again - it works like a charm!

I'm embarrassed to say that my error was that I had some environment variables defined in my terminal from a previous example. :blush:

JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM=true
JAVASCRIPTKIT_USE_LEGACY_RESOURCE_BUNDLING=1

I assume that it was the latter one that caused the issue.

1 Like

I'm glad it worked! The only remaining caveat is that for async I recommend switching to 6.3 snapshots, unfortunately we had issues with async support in 6.2 release cycle and fixes didn't end up in patch releases. 6.3 snapshots should work in the meantime, and also we expect them to be much more stable than main snapshots.

1 Like

Looking forward to using async too, but so far I have not had use for that in the simple game. :-)

It's a very simple project of making a small game on a 10 x 6 LED matrix together with my daughter.

The ESP32C6 project can be seen here.

and the Wasm version here:

I ended up making the UI with ElementaryUI (https://elementary.codes)

So now the game is running on iPad (in Playgrounds), ESP32C6 and the browser.
I ought to add Android, Windows and more Apple platforms for completion. :slight_smile:

3 Likes

ElementaryUI mentioned, let's goooo!!!! ─=≡Σ((( つ•̀ω•́)つ

pro tip:
you could use the .receive(GlobalDocument.onKeyDown) mechanism and handle key events.

example here:

3 Likes

Awesome, @sliemeobn - I did try various other attempts at adding keyboard event handling. :slight_smile:

It's now added and online.

There are three small games (so far): Labyrinth, Digital Pet (WIP) and a memory game.

You select next game by tapping 'Select' (s or Enter) three times.
Space / f is a 'fire' / 'action' button for the games that use it.

Thanks again!