This Gist builds on https://gist.github.com/unconed/4370822 from @unconed.
Instead of the original method which writes to the browsers sandboxed filesystem here we use a websocket connection provided by websocketd to pipe image data to a short python script that writes out the .png files to disk.
https://github.com/joewalnes/websocketd
After installing it and saving the below code snippets to canvascapture.html and canvascapture.py you will launch the websocket server like this:
websocketd --port=8080 ./canvascapture.py
The below combines the original raf override from @unconed, the example code from websocketd and the introductory scene code from threejs.org.
From the same directory to which you save the html launch a server and load it in your browser:
python -m SimpleHTTPServer 8000
<html>
<head>
<meta charset=utf-8>
<title>My first Three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="js/three.min.js"></script>
<script>
var ws = new WebSocket('ws://localhost:8080/');
ws.onopen = function() {
console.log('CONNECT');
};
ws.onclose = function() {
console.log('DISCONNECT');
};
ws.onmessage = function(event) {
console.log('MESSAGE: ' + event.data);
};
var frames = 1000;
// Request Animation Frame Override
var raf = window.requestAnimationFrame;
var next = null;
var hold = false;
window.requestAnimationFrame = function rafOverride(callback) {
// Find canvas
var canvas = document.querySelector('canvas');
if (canvas) {
// Done capturing?
if (frames < 0) {
window.requestAnimationFrame = raf;
return raf(callback);
}
// Hold rendering until screenshot is done
if (!hold) {
hold = true;
frames--;
setTimeout(function () {
callback();
capture(canvas, function () {
// Resume rendering
hold = false;
rafOverride(next);
});
}, 5);
}
else {
next = callback;
}
}
else {
// Canvas not created yet?
return raf(callback);
}
}
function capture(canvas, callback) {
// Capture image and strip header from string.
var image = canvas.toDataURL('image/png').slice(22);
// Writing image as msg
ws.send(image);
setTimeout(function () {
// Resume rendering
callback();
}, 5);
}
// Three js scene
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.BoxGeometry( 1, 1, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var cube = new THREE.Mesh( geometry, material );
scene.add( cube );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
cube.rotation.x += 0.1;
cube.rotation.y += 0.1;
renderer.render( scene, camera );
}
render();
</script>
</body>
</html
Copy the below into a file named canvascapture.py and then make it executable
chmod +x canvascapture.py
#!/usr/bin/env python
'''
Writes out data received from websocketd on stdin as
png frames.
'''
import time
import base64
import binascii
from sys import stdin, stdout
# From http://stackoverflow.com/a/9807138
def decode_base64(data):
"""Decode base64, padding being optional.
:param data: Base64 data as an ASCII byte string
:returns: The decoded byte string.
"""
missing_padding = 4 - len(data) % 4
if missing_padding:
data += b'='* missing_padding
return base64.decodestring(data)
def main():
'''
As each frame is received write it to a .png file
'''
frames = 0
while True:
received_val = stdin.readline()
# Right pad filenames so they can be sorted
filename = str(time.time()).ljust(13, "0") + ".png"
with open(filename, 'wb') as file_handle:
try:
file_handle.write(decode_base64(received_val))
frames += 1
if frames % 10 == 0:
print("Captured %d frames ..." % frames)
stdout.flush()
except binascii.Error:
# Ignore malformed data urls
continue
if __name__ == '__main__':
main()