WebGL Snapshot setup

Hello!

I’m struggling to get the WebGL Snapshot package set up properly; I see that I’m not the only one since it was posted and that some questions are still left hanging. Could you share a simple vanilla example to get us all started properly? :slight_smile:

I’m starting from a blank A-Frame > Face Tracking > Standalone HTML template, but I’m confused by the installation guidelines.
Do we agree that installation is EITHER via npm and then import, OR via CDN by linking to the script (and then no need to import)? The CDN instructions saying “once you’ve installed the library, reference the script in your html” kind of confuses me, as it implies it should be done one after the other.
I see that the guidelines for the somewhat similar VideoRecorder comfort my feeling (i.e. it’s either one or the other, and the CDN link is pretty self-sufficient without further need to import).
Am I correct?

But based on this, I still cannot get it to work, neither through npm or CDN (which I’d prefer).
Could you help me correct what is wrong in these 2 approaches below?
I’m using the snippets from the webgl-snapshot npm readme rather than the dedicated page in the Zapworks docs as it seems more up-to-date (for instance it refers to PromptImage() which seems deprecated in the last updates, if I understand well?)

— METHOD 1: NPM —

Installation

npm install @zappar/webgl-snapshot

Usage

I ran into several troubles based on the default instructions:

1.

I think the recommended import:
import snapshot from '@zappar/webgl-snapshot'

should rather be:
import snapshot from './node_modules/@zappar/webgl-snapshot'
based on the default npm location modules are installed to

Otherwise I ran into an Uncaught TypeError: Failed to resolve module specifier "@zappar/webgl-snapshot" error.

2.

Here is my “app.js” file (I have a basic #snapshot-button on my HTML index)

import snapshot from './node_modules/@zappar/webgl-snapshot'

document.addEventListener("DOMContentLoaded", function() {

    document.getElementById('snapshot-button').addEventListener("click", function(e){
        
        console.log("snapshot clicked");

        // Get canvas from dom (>> modified for A-Frame)
        //const canvas = document.querySelector('canvas');
        const canvas = document.querySelector("a-scene").components.screenshot.getCanvas("perspective");

        // Convert canvas data to url
        const url = canvas.toDataURL('image/jpeg', 0.8);

        // Take snapshot
        snapshot({
            data: url,
        });

    }, false);

})

If I link to this file with <script src="app.js"></script>, I’m thrown Uncaught SyntaxError: Cannot use import statement outside a module

So I use: <script type="module" src="app.js"></script>

But I’m still thrown: Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of "text/html". Strict MIME type checking is enforced for module scripts per HTML spec.

And I’m too little familiar with modules to go any further…

— METHOD 2: CDN (the one I’m rather interested in) —

Link to script

<script src="https://libs.zappar.com/zappar-snapshot/0.0.18/zappar-snapshot.min.js"></script>

Usage

Since we don’t have a “snapshot” import alias like we did in the NPM method, I looked into the minified JS code and think that ZapparWebGLSnapshot() is what we want to call, but it throws an error that ZapparWebGLSnapshot is not a function, so obviously someting is wrong or missing.

Here is my “app.js” file:

document.addEventListener("DOMContentLoaded", function() {

    document.getElementById('snapshot-button').addEventListener("click", function(e){
        
        console.log("snapshot clicked");

        // Get canvas from dom (>> modified for A-Frame)
        //const canvas = document.querySelector('canvas');
        const canvas = document.querySelector("a-scene").components.screenshot.getCanvas("perspective");

        // Convert canvas data to url
        const url = canvas.toDataURL('image/jpeg', 0.8);

        ZapparWebGLSnapshot({
            data: canvas.toDataURL('image/jpeg', 0.8)
        });

    }, false);

})

Would be really great to have all this better documented, as this is a very desired feature for a lot of us I’m sure :slight_smile:

Thanks for your assistance!
Thomas

Hi @thomas2,

The installation method depends on your project setup. If you’re using webpack (and therefore node), you should use NPM.

If you’re using standalone, you need to use CDN.


" — METHOD 1: NPM —": import snapshot from '@zappar/webgl-snapshot'

This is the correct way of importing the module into a node/webpack (not standalone) project. I would reccomend reading the nodejs documentation for clarification on how to use node modules.

You should not be using this method in your standalone project.

" — METHOD 2: CDN —":

As of v0.0.18:
You’re correct in finding the ZapparWebGLSnapshot name. Try adding .default to it to get the default exported function:
ZapparWebGLSnapshot.default({... .

I understand that the documentation could be clearer. The next patch should clear things up! :slight_smile:

Thanks for the feedback!

edit:

@zappar/webgl-snapshot 0.0.21 released:

  • Standardizes the module’s namings across NPM and CDN. (the .default part is no longer required).
  • Updated README to reflect the above.
  • As per your feedback, added the “cdn OR npm” disclaimer to the README.

You can view the latest README here: https://www.npmjs.com/package/@zappar/webgl-snapshot

The readme changes will make their way to the zapworks docs shortly :slight_smile:

1 Like

Hey (again) Deim,

Thanks a lot for confirming this and for the update :tada:

Here’s a few minor things I spotted in the npm docs you pointed:

  • this is actually what really confused me about the npm and/or CDN:

image

  • And further below you still have references to the now deprecated promptImage method that might be confusing :slight_smile:

image

Hope this helps!

It works out of the box with the correct method name now :slight_smile:

But (that would be too easy):

  1. the IG share did not go through.

I personnally don’t leave permissions for IG to access storage on my phone; I guess this is needed as the photo is probably stored locally before being uploaded? If this is the problem, maybe it would be worth to have a message delivered to the user “you need to give IG permissions to access storage to share the picture”? Or do we just have no way to know whether the request succeeded or not?

  1. the snapshot (both in the preview and saved image) is saved in landscape mode (4096x2048px), while I’m on portrait orientation (I’m usingh the default Face Tracking A-Frame template), so it is all stretched out.
  • is the snapshot supposed to be at the same resolution as the viewport? (in which case we have a bug here)
  • or do we have to / can override the saved image resolution manually? I didn’t find any relevant width/height attribute when searching the minified JS file.

Thanks for this additional info!
T

Hah, thanks for spotting.

We don’t really know which app the user selects for sharing (among many reasons it could fail), so can’t give very specific instructions.

This lib does not do any form of image manipulation, the resolution/orientation comes from AFrame. Looks like you should be able to change those values. :slight_smile:

I was rather talking about a message after a (failed) share (we don’t have to guess which app then :innocent:). To rephrase, my question was: in the IG API, doesn’t the function return a “on success” boolean and/or verbose error message depending on whether the sharing succeeded, so we can inform the user if and why it failed?

Indeed, for some reason the A-Frame canvas we fetch is not screen size but a default 4096x2048.

To whoever is looking for this, here is my modified snippet to make the capture the same size as the display:
(would be great to add such snapshot/videorec capabilities by default in the project galleries imho :upside_down_face:)

document.addEventListener("DOMContentLoaded", function() {

    document.getElementById('snapshot-button').addEventListener("click", function(e){
        
        console.log("snapshot clicked");

        var width = window.innerWidth
        || document.documentElement.clientWidth
        || document.body.clientWidth;

        var height = window.innerHeight
        || document.documentElement.clientHeight
        || document.body.clientHeight;

        // Get canvas from dom
        document.querySelector("a-scene").setAttribute('screenshot', {
            width: width,
            height: height
        })
        const canvas = document.querySelector("a-scene").components.screenshot.getCanvas("perspective");

        // Convert canvas data to url
        const url = canvas.toDataURL('image/jpeg', 0.8);

        ZapparWebGLSnapshot({
            data: url,
        });

    }, false);

})

One bug I noticed though is that once the snapshot is taken, the preview shown is displayed full-width (with margin) in the “share or close” screen, with no scrolling enabled, which means that if for some reason the capture is in portrait while the screen is landscape, the preview is overflowing and we don’t have access to the buttons.

I noticed that when testing on my desktop to set specific width/height for the capture (see screenshot attached, with an empty black 1024x1024 canvas for the test).

I agree most of the time the capture will be at screen ratio, but who knows? To be bullet-proof it would be worth having the preview being displayed at (full width if snapshot is landscape) or (full height if snapshot is portrait) to ensure it is displayed in full.

image

1 Like