Sketchfab Viewer API with AlpineJS

Hi,

I would like to use the Sketchfab viewer api with AlpineJS so I can build a reactive UI for some api calls, like material changes. Is it possible?

When I initialize Sketchfab within Alpine, I get a cross-origin error:

Uncaught DOMException: Blocked a frame with origin “http://127.0.0.1:5500” from accessing a cross-origin frame.
at _t (http://127.0.0.1:5500/alpine.min.js:5:20049)
at Object.get (http://127.0.0.1:5500/alpine.min.js:5:15348)
at Proxy.use (http://127.0.0.1:5500/sketchfab.js:1:2186)
at Proxy._initializeAPIEmbed (http://127.0.0.1:5500/sketchfab.js:1:6481)

This also happens on a live server, so it’s not a local development CORS problem.

This is the code I’m starting with:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    
    <script defer src="https://unpkg.com/alpinejs@3.5.0/dist/cdn.min.js"></script>
    <script type="text/javascript" src="https://static.sketchfab.com/api/sketchfab-viewer-1.10.1.js"></script>

</head>

<body>
    <script>

        function viewerData() {
            return {
                client: null,
                initUid: '2aff33cc33d74b56ac0009fbca65093c',
                init() {
                    this.client = new Sketchfab(this.$refs.vieweriframe);
                    this.client.init(this.initUid, {
                        success: this.onSuccess,
                        error: this.onError,
                        camera: 1,
                    });
                },
                onSuccess(api) {
                    console.log(api);
                    // api.addEventListener('viewerready', this.apiReady(api));
                },
                onError() {
                    
                },
                apiReady(api) {
                    console.log(api);
                },

            }
        }
    </script>

    <div x-data="viewerData()">
        <iframe src="" id="api-frame" x-ref="vieweriframe" class="sfab-iframe"
            allow="autoplay; fullscreen; xr-spatial-tracking" xr-spatial-tracking execution-while-out-of-viewport
            execution-while-not-rendered web-share allowfullscreen mozallowfullscreen="true"
            webkitallowfullscreen="true"></iframe>
    </div>

</body>

</html>

Hey there !

I’m not particularly fluent with Alpine, but generally speaking this should be doable. For instance, one of our partners did something in React that could be helpful, you can find details here https://blog.theodo.com/2020/04/sketchfab-react-part-1/

I’m not sure why a CORS error would appear here ; At no point should the iframe have such an origin (localhost) if the viewer API initializes it. Is it possible that your environment makes it so that you whole app is itself within an iframe, which could add a layer of issues ? Or something of that nature.

Without advanced knowledge of Alpine I can’t say for sure but in terms of logic the snippet you pasted seems sound to me.

In any case, there should not be a CORS error at all during interaction with the Sketchfab Viewer API so I’d suspect the root cause of the issue to be elsewhere in the stack.

Thank you for the response and the link, both were helpful for troubleshooting!

This helped me figure out how to extract the api object out of the client.init() method.

Here is a working example in case it helps anyone:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>

    <script defer src="https://unpkg.com/alpinejs@3.5.0/dist/cdn.min.js"></script>
    <script type="text/javascript" src="https://static.sketchfab.com/api/sketchfab-viewer-1.10.1.js"></script>

</head>

<body>
    <script>

        function viewerData() {
        return {
            initUid: '2aff33cc33d74b56ac0009fbca65093c',
            apiRef: null,
            changeBackgroundColor() {
                this.apiRef.setBackground({
                    color: [Math.random(), Math.random(), Math.random(), 1],
                });
            },
            init() {
                self = this;
                let client = new Sketchfab(this.$refs.vieweriframe);
                client.init(this.initUid, {
                    success: function (api) {
                        self.apiRef = api;
                        self.apiRef.start();
                    },
                    error: function() {
                    }

                    camera: 1,
                });
            },
            
        }
    }
    </script>

    <div x-data="viewerData()">
        <iframe src="" id="api-frame" x-ref="vieweriframe" class="sfab-iframe"
            allow="autoplay; fullscreen; xr-spatial-tracking" xr-spatial-tracking execution-while-out-of-viewport
            execution-while-not-rendered web-share allowfullscreen mozallowfullscreen="true"
            webkitallowfullscreen="true"></iframe>
        <button @click="changeBackgroundColor">Change Background Color</button>
    </div>

</body>

</html>

Glad to hear it ! I’ll keep your sample in mind in case the subject comes up again :slight_smile:

Cheers !