Calls between JavaScript and WebAssembly are finally fast
Firefox loads and runs Unity3D WebGL apps MUCH faster than Chrome.<p>The point of UnityJS is to tightly and efficiently integrate Unity3D and JavaScript, so it does a lot of JavaScript &lt;=&gt; C# calls, and I&#x27;m looking forward to it getting even faster!<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS</a><p>You can pass delegates to C# functions that are directly callable into JavaScript using some magic PInvoke attributes and the Unity Runtime.dynCall function.<p>Declare a delegate that describes the signature of your C# function you want to call from JavaScript:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Assets&#x2F;Libraries&#x2F;UnityJS&#x2F;Scripts&#x2F;BridgeTransportWebGL.cs#L35" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Ass...</a><p><pre><code>    public delegate int AllocateTextureDelegate(int width, int height);
</code></pre>
Then declare a C# static method with the MonoPInvokeCallback attribute, to implement you C# function:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Assets&#x2F;Libraries&#x2F;UnityJS&#x2F;Scripts&#x2F;BridgeTransportWebGL.cs#L82" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Ass...</a><p><pre><code>    [MonoPInvokeCallback(typeof(AllocateTextureDelegate))]
    public static int AllocateTexture(int width, int height) { ... }
</code></pre>
Then pass those specially marked delegates to JavaScript and stash them in JS variables when you initialize (it doesn&#x27;t work unless you use the magic MonoPInvokeCallback attribute):<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Assets&#x2F;Libraries&#x2F;UnityJS&#x2F;Scripts&#x2F;BridgeTransportWebGL.cs#L48" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Ass...</a><p><pre><code>    [DllImport(PLUGIN_DLL)]
    public static extern void _UnityJS_HandleAwake(AllocateTextureDelegate allocateTextureCallback, FreeTextureDelegate freeTextureCallback, LockTextureDelegate lockTextureCallback, UnlockTextureDelegate unlockTextureCallback);
</code></pre>
<a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Assets&#x2F;Libraries&#x2F;UnityJS&#x2F;Scripts&#x2F;BridgeTransportWebGL.cs#L175" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Ass...</a><p><pre><code>    public override void HandleAwake()
    {
        &#x2F;&#x2F;Debug.Log(&quot;BridgeTransportWebGL: HandleAwake: this: &quot; + this + &quot; bridge: &quot; + bridge);

        _UnityJS_HandleAwake(
            AllocateTexture,
            FreeTexture,
            LockTexture,
            UnlockTexture);
    }
</code></pre>
In the awake function on the JavaScript side of your Unity WebGL extension (a .jslib file), wrap the C# delegate in a JavaScript thunk that calls into it via Runtime.dynCall:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Assets&#x2F;Libraries&#x2F;UnityJS&#x2F;Plugins&#x2F;WebGL&#x2F;UnityJS.jslib#L11" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;master&#x2F;UnityJS&#x2F;Ass...</a><p><pre><code>    &#x2F;&#x2F; Called by Unity when awakened.
    _UnityJS_HandleAwake: function _UnityJS_HandleAwake(allocateTextureCallback, freeTextureCallback, lockTextureCallback, unlockTextureCallback)
    { [...]
        function _UnityJS_AllocateTexture(width, height)
        {
            &#x2F;&#x2F;console.log(&quot;UnityJS.jslib: _UnityJS_AllocateTexture: width: &quot; + width + &quot; height: &quot; + height + &quot; allocateTextureCallback: &quot; + allocateTextureCallback);
            var result = Runtime.dynCall(&#x27;iii&#x27;, allocateTextureCallback, [width, height]);
            &#x2F;&#x2F;console.log(&quot;UnityJS.jslib: _UnityJS_AllocateTexture: result: &quot; + result);
            return result;
        };
        window.bridge._UnityJS_AllocateTexture = _UnityJS_AllocateTexture;
</code></pre>
Then you can call the C# method from JavaScript:<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;f0a4e1fb07fd9e8aaba2849fefd1d2239c262a97&#x2F;UnityJS&#x2F;Assets&#x2F;StreamingAssets&#x2F;game.jss#L348" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;f0a4e1fb07fd9e8aab...</a><p><pre><code>    params.cache.backgroundSharedTextureID = id = 
        window.bridge._UnityJS_AllocateTexture(params.width, params.height);
</code></pre>
This is zillions of time faster and more flexible than using Unity&#x27;s terrible SendMessage technique to send messages from JS=&gt;C#, whose only parameter is a single string, and which inefficiently dispatches messages by looking up Unity objects by name, and is asynchronous and can&#x27;t return a result.<p>I use this technique to efficiently copy binary textures and arrays of numbers between JavaScript and C#. MUCH better than serializing it as JSON, or base 64 encoded PNG files in a data: url (yuck!).<p><a href="https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;674eda49be12a7081205b6af4bf3986cb76ac7d7&#x2F;UnityJS&#x2F;Assets&#x2F;StreamingAssets&#x2F;game.jss#L382" rel="nofollow">https:&#x2F;&#x2F;github.com&#x2F;SimHacker&#x2F;UnityJS&#x2F;blob&#x2F;674eda49be12a70812...</a><p><pre><code>    function DrawToCanvas(params, drawer, success, error)
    { [...]

                    var id = params.pie.backgroundSharedTextureID;
                    if (!id) {
                        params.pie.backgroundSharedTextureID = id = 
                            window.bridge._UnityJS_AllocateTexture(params.width, params.height);
                        &#x2F;&#x2F;console.log(&quot;game.js: DrawToCanvas: WebGL: AllocateTexture: width: &quot; + params.width + &quot; height: &quot; + params.height + &quot; id: &quot; + id);
                    }
                    var imageData =
                        context.getImageData(0, 0, params.width, params.height);
                    window.bridge._UnityJS_UpdateTexture(id, imageData);
                    texture = {
                        type: &#x27;sharedtexture&#x27;,
                        id: id
                    };
                    success(texture, params);
                    canvasNode.parentNode.removeChild(canvasNode);
</code></pre>
This lets me draw 2D user interface stuff, pie charts, diagrams, data visualizations, etc, in JavaScript with canvas, d3, or whatever library I like, and then efficiently use those images in Unity3D as user interface overlays, 3D textures, etc. It works great, and it&#x27;s smooth and interactive, mixing up 2D canvas graphics with 3D Unity stuff!<p>Unity is sorely lacking a decent 2D drawing library like canvas, not to mention fancy stuff built on top of it like d3.<p>I&#x27;m currently working on the plumbing to send binary arrays of floats from JavaScript to Unity, so I can pass them right into shaders!<p>Here&#x27;s some discussion about the magic MonoPInvokeCallback attribute:<p><a href="https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;monopinvokecallback-in-unity.132510&#x2F;" rel="nofollow">https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;monopinvokecallback-in-unity...</a><p>And about Unity.dyncall and the WebGL runtime:<p><a href="https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;c-jslib-2-way-communication.323629&#x2F;" rel="nofollow">https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;c-jslib-2-way-communication....</a><p>and:<p><a href="https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;super-fast-javascript-interaction-on-webgl.382734&#x2F;" rel="nofollow">https:&#x2F;&#x2F;forum.unity.com&#x2F;threads&#x2F;super-fast-javascript-intera...</a>