# How can I add a screenshot button to the Three.js Editor?

**URL:** <https://discourse.threejs.org/t/how-can-i-add-a-screenshot-button-to-the-three-js-editor/57300>\
**Category:** Questions\
**Tags:** screenshot, editor\
**Created:** [October 22, 2023, 3:12pm UTC](https://discourse.threejs.org/t/how-can-i-add-a-screenshot-button-to-the-three-js-editor/57300 "2023-10-22T15:12:37Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Bob55](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@Bob55](https://discourse.threejs.org/u/Bob55)\
**Post date:** [October 22, 2023, 3:12pm UTC](https://discourse.threejs.org/t/how-can-i-add-a-screenshot-button-to-the-three-js-editor/57300/1 "2023-10-22T15:12:37Z")

</div>

I would like to modify the three.js [editor](https://threejs.org/editor/) to add a button to take screenshots of the scene.

Basically I want to emulate what has been implemented here, where you can see “Capture Image” option at the bottom of the “File” menu: [http://editor.xtra-d.co.uk/standalone-editor/](http://editor.xtra-d.co.uk/standalone-editor/)

I also found a tool named “Online 3D Viewer” live here: ([https://3dviewer.net](https://3dviewer.net/))  
Source code here: [GitHub - kovacsv/Online3DViewer: A solution to visualize and explore 3D models in your browser.](https://github.com/kovacsv/Online3DViewer) which is based on three.js an has functionality for taking a screenshot/snapshot.

What I have managed to do so far is to overlay a button with an image on top of the three.js scene. And this button listens to click events:

![threejs_screenshot_button](https://canada1.discourse-cdn.com/flex035/uploads/threejs/original/3X/5/c/5c6418b452aaf50b5b499b70af62b151751a0c39.gif)

By googling I found out that the canvas element can be used to take a screenshot of a three.js scene. In the bellow example this works fine since the renderer object is in the same file where the function for taking the screenshot is defined:

Original question here: [javascript - Three.js: How can I make a 2D SnapShot of a Scene as a JPG Image? - Stack Overflow](https://stackoverflow.com/questions/26193702/three-js-how-can-i-make-a-2d-snapshot-of-a-scene-as-a-jpg-image)  
Full working example code here: [https://codepen.io/shivasaxena/pen/QEzrrv](https://codepen.io/shivasaxena/pen/QEzrrv)

```javascript
[code shortened]
renderer = new THREE.WebGLRenderer({
            preserveDrawingBuffer: true
        });
[code shortened]

function saveAsImage() {
        var imgData, imgNode;

        try {
            var strMime = "image/jpeg";
            imgData = renderer.domElement.toDataURL(strMime);

            saveFile(imgData.replace(strMime, strDownloadMime), "test.jpg");

        } catch (e) {
            console.log(e);
            return;
        }

    }

    var saveFile = function (strData, filename) {
        var link = document.createElement('a');
        if (typeof link.download === 'string') {
            document.body.appendChild(link); //Firefox requires the link to be in the body
            link.download = filename;
            link.href = strData;
            link.click();
            document.body.removeChild(link); //remove the link when done
        } else {
            location.replace(uri);
        }
    }

```

**The problem I am facing is that I don’t know how to access the canvas or the renderer object from inside my file** (which I named `Screenshot.js`) where I have the code to take the screenshot.

What I have done so far is create a `Screenshot.js` file:

```javascript

import { UIPanel, UIButton } from './libs/ui.js';

function Screenshot( editor ) {

	const strings = editor.strings;

	const container = new UIPanel();
	container.setId( 'screenshot' );
    container.setPosition( 'absolute' );
	container.setRight( '620px' );
	container.setTop( '40px' );

	// screenshot

	const screenshotIcon = document.createElement( 'img' );
	screenshotIcon.title = strings.getKey( 'screenshot' );
	screenshotIcon.src = 'images/screenshot.svg';

	const screenshotButton = new UIButton();
	screenshotButton.dom.appendChild( screenshotIcon );
	screenshotButton.onClick( function () {

		console.log("Screenshot from Screenshot,js");

        const canvas = document.getElementById("myCanvas");

        if (canvas) {
            console.log('Debug click screenshot');
        
            canvas.toBlob(function (blob) {
                // Create a link for downloading the Blob
                const a = document.createElement('a');
                a.href = URL.createObjectURL(blob);
                a.download = 'image.png';
        
                // Programmatically trigger a click event to initiate the download
                a.click();
            }, 'image/png');
        } else {
            console.error("Canvas element not found.");
        }

    }

);

	container.add( screenshotButton );

	return container;

}

export { Screenshot };

```

And I modified the original index.html file to assign an id to the canvas element:

```javascript
[code shortened]
<body ontouchstart="">
		<script type="module">

			import * as THREE from './js/three.module.js';
			import { APP } from './js/app.js';
			import { VRButton } from './js/VRButton.js';

			window.THREE = THREE; // Used by APP Scripts.
			window.VRButton = VRButton; // Used by APP Scripts.

			var loader = new THREE.FileLoader();
			loader.load( 'app.json', function ( text ) {

				var player = new APP.Player();
				player.load( JSON.parse( text ) );
				player.setSize( window.innerWidth, window.innerHeight );
				player.play();

                // Here I assign an id to the canvas element
                player.dom.id = "myCanvas";
                
				document.body.appendChild( player.dom );

				window.addEventListener( 'resize', function () {

					player.setSize( window.innerWidth, window.innerHeight );

				} );

			} );

			/* edit button */

		</script>
	</body>
[code shortened]

```

Unfortunetely the above method for assigning the “myCanvas” ID to the player.dom canvas element results in the “Canvas element not found.” message displayed to the console.

Can anyone help? Greatly appreciate any hints or insights you can provide. Thanks in advance!

---

<div class="post-metadata">

**Author:** ![Mugen87](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/mugen87/32/5645_2.png) [@Mugen87](https://discourse.threejs.org/u/Mugen87)\
**Post date:** [October 23, 2023, 9:19am UTC](https://discourse.threejs.org/t/how-can-i-add-a-screenshot-button-to-the-three-js-editor/57300/2 "2023-10-23T09:19:11Z")

</div>

I would try to implement it like so:

- Create a new signal in `Editor.js` called `makeScreenshot`.
- Dispatch this signal in `Screenshot.js` when the button is clicked.
- Add an event handler to `Viewport.js`. This is the only component that has access to the editor’s renderer. The handler will the use the `renderer.domElement` canvas reference to make a screenshot.

But to make it clear, making screenshots like this would only work in the editor, not in exported apps.

---

<div class="post-metadata">

**Author:** ![Bob55](https://avatars.discourse-cdn.com/v4/letter/b/e9c0ed/32.png) [@Bob55](https://discourse.threejs.org/u/Bob55)\
**Post date:** [October 28, 2023, 8:36am UTC](https://discourse.threejs.org/t/how-can-i-add-a-screenshot-button-to-the-three-js-editor/57300/3 "2023-10-28T08:36:50Z")

</div>

Hi @Mugen87,

By following your pointers, I was able to make it work!  
Thank you, thank you so much for taking the time to answer!

For others to reference, these are the changes I made:

**Editor.js**

```javascript
        // screenshot
       makeScreenshot: new Signal(),

```

**Screenshot.js**

```javascript
import { UIPanel, UIButton } from './libs/ui.js';

function Screenshot( editor ) {

	const strings = editor.strings;
	const container = new UIPanel();
	container.setId( 'screenshot' );
    container.setPosition( 'absolute' );
	container.setRight( '620px' );
	container.setTop( '40px' );

	const screenshotIcon = document.createElement( 'img' );
	screenshotIcon.title = strings.getKey( 'screenshot' );
	screenshotIcon.src = 'images/screenshot.svg';

	const screenshotButton = new UIButton();
	screenshotButton.dom.appendChild( screenshotIcon );
	screenshotButton.onClick( function () {
        editor.signals.makeScreenshot.dispatch();

    }

);

	container.add( screenshotButton );

	return container;

}

export { Screenshot };

```

**Viewport.js**

```javascript
signals.makeScreenshot.add(function () {

    var screenshotDataUrl;
    try {
        var strMime = "image/jpeg";
        render();
        screenshotDataUrl = renderer.domElement.toDataURL(strMime);

        var link = document.createElement('a');
        if (typeof link.download === 'string') {
            // Firefox requires the link to be in the body
            document.body.appendChild(link);
            const filename = "screenshot" + ".jpg";
            link.download = filename;
            link.href = screenshotDataUrl.replace(strMime, "image/octet-stream");
            link.click();
            document.body.removeChild(link);
        } else {
            location.replace(uri);
        }
    } catch (e) {
        console.log(e);
        return;
    }
});

```

**index.html**

```javascript
import { Screenshot } from './js/Screenshot.js';

// code shortened
 const screenshot = new Screenshot( editor );
 document.body.appendChild( screenshot.dom );

```

Also added an entry to `Strings.js`, `main.css` and the `screenshot.svg` file.
