Advertisement

You're blocking ads, which pay for BlenderNation. Read about other ways to support us.

Controlling Blender from the Browser

8

Hi, my name is Torsten. I’m a freelancer from Berlin, Germany. I spent most of life using Cinema 4D, but discovered Blender two years ago for a web-project where we needed to run a 3D software on a webserver and to control it via PHP. This is the story of it.

WARNING: some programming and Linux knowledge required

When my client asked me how we could automate adding customized elements into existing videos, I had some ideas how that might work theoretically. I had already worked with ffmpeg on a Linux webserver, encoding large MP4 master files into smaller streamable videos on demand via a browser interface. I knew that ffmpeg was able to cut video files together, add images, rotate stuff, place a video inside another video; but in this case the requirements were more complicated. After some failed experiments I came to the conclusion that I needed motion tracking. So I began searching for a software that offered motion tracking, could load a video file, add stuff to it and save it again - on a Linux webserver with the console. Eventually I stumbled upon Blender and fell in love.

The basic train of thought goes like this

  1. PHP can start programs on the Linux console (or shell)
  2. Blender is a program which can be run from the console with custom options and commands
  3. Programs can be run in the background (which means that you start a program and immediately get back to the console and start other programs)
  4. Text output from programs can be directed to text files
  5. Every running program (process) has a PID (process ID) that allows you to control it
  6. PHP can create or change images (like texture maps) and text files (like Python scripts which can be loaded at startup)

These neat facts allow us to start Blender with a click in the browser and render an animation. We can see the render progress and stop Blender if we want or receive an email when rendering has finished.

What you need to know

  1. Basic PHP
  2. Basic HTML, CSS and little bit of Javascript
  3. Blender, of course
  4. Basic bash Linux commands

What you need to have

  1. Webserver running Linux with PHP and Imagick image lib
  2. Shell root access via SSH
  3. Patience :)

What you need to install on a Linux webserver to run Blender

  1. mesa-libGLU
  2. glibc 2.24 (for Blender 2.80)
  3. GCC to compile glibc 2.24

Notice: we use CPU rendering in Cycles. For GPU rendering additional libraries may be required.

Depending on your Linux package manager the commands to install may vary, but on AWS (Amazon), for example, the commands are:

yum install mesa-libGLU

GCC is required to compile glibc 2.24 which is used by Blender 2.80. If you plan to use an older version of Blender, you can skip this.

yum install gcc

So far so easy. The next step is more complicated and error prone. Compiling. Yeah.

#Install glibc 2.24
wget https://ftp.gnu.org/gnu/libc/glibc-2.24.tar.gz
tar -xvzf ./glibc-2.24.tar.gz
cd glibc-2.24
mkdir _build
cd _build
../configure --enable-kernel=3.13 --prefix=/usr
make
make install

Go to your web servers root directory and create a directory for our project called “blender”.

cd <where ever the root of your server is>
mkdir blender

Download Blender from blender.org by copying the link for Linux (check if your kernel is 32- or 64-bit) and paste it to the console.

#download
wget https://builder.blender.org/download/blender-2.80-54ffc4e19dc4-Linux-glibc224-x86_64.tar.bz2
 
#unpack
tar -xjf blender-2.80-54ffc4e19dc4-Linux-glibc224-x86_64.tar.bz2
 
#shorten dir name
mv blender-2.80-54ffc4e19dc4-Linux-glibc224-x86_64 blender
 
#check if Blender runs
./blender/blender --version

If everything worked we should see this:

Blender 2.80 (sub 45)
	build date: 2019-02-26
	build time: 00:28:55
	build commit date: 2019-02-25
	build commit time: 20:59
	build hash: 54ffc4e19dc4
	build platform: Linux
	build type: Release

Congrats! You’ve made it. Now the fun part starts.

Prepare your .blend file

For this tutorial we want to prepare a test scene. Maybe the default cube with bevel and some textures.

I will use a texture and HDRI for lighting from texturehaven.com.

Be sure to download the PNG, which can be re-saved without quality loss. I also scaled the UV up so that the texture covers one cube face completely.

Basic scene setup

Make sure the paths to the texture maps are all relative. If you upload your .blend to the server, all external files should stay in the same location relative to your .blend.

We do not want to pack them (unlike when using a render farm), because we may want to manipulate the textures before rendering.

For the sake of this tutorial I will not cover security concerns, so I assume you are using a secure closed system, only accessible by you.

Using Blender from the console

One word of advice regarding testing: using external servers or cloud services like AWS costs money. To practise using Blender via text commands you should always start locally, just like web developers usually have a local webserver running on their laptop, before they deploy a website to an online server.

Non-Linux users need to find the program’s binary in order to do that. On OS X it’s hidden inside the Blender.app “file” (actually a directory) in “Contents/MacOS/”. Copy the whole “Blender.app” to our project directory. All the commands we use will be called from there.

To make the code easier to read I will omit the actual path to Blender, because it is different in Linux, Windows or OS X. Instead I will just write “blender”.

The most helpful command to begin with is:

blender --help

This will list all available commands and options (which are a lot). The most important ones are:

  1. -b = do not open the Blender UI window
  2. -o = where to save the renders (optional but recommended, overwrites settings in the .blend file)
  3. -a = render an animation
    -f = or render just one frame, example -f 1 (renders frame 1)

These commands need to come in the order they will be executed. First we tell Blender to just run in the console (-b) and not open its UI. Then we tell it which .blend file to load, after that we set up the rendering output (-o) and as the last command we tell it to start rendering (-f). We need to tell Blender where to save the image before we let it render the image. So rendering will almost always be the last command.

Assuming that we have cd’d into the directory where our test.blend lies, and created a dir called “results”, we can start rendering with this command:

blender -b test.blend -o results/test####.png -f 1

The #### in the output filename will be replaced with the frame number. If you don’t add that, Blender will append 0001.png to your output file path, which results in ugly filenames like “test.png0001.png”. 4 times # represents 4 digits. If your animation has more than 9999 frames just type #####.

Running this command should produce an image “test0001.png” in the results directory.

Generating output

When Blender runs in the console it produces a lot of text output and, besides error messages, what we are looking for are lines like this:

Fra:1 Mem:81.19M (0.00M, Peak 91.12M) | Time:00:34.33 | Remaining:00:00.97 | Mem:34.43M, Peak:44.36M | Scene, View Layer | Rendered 492/510 Tiles

There we have the frame being rendered (Fra: 1), memory usage, time and, at the end of the line, Rendered Tiles (of use when you just render one big frame). Basically the same output you can see in the UI in the render results window.

Since our ultimate goal is to control Blender from a browser we need to read this output with a script. One basic functionality in the console is to direct text output from programs to log files:

Some_program > its_output.log

In our case we name the log file according to our project “test”:

blender -b test.blend -o results/test####.png -f 1 > test.log

While we’re at it, we can also tell the program to be silent, give us its process id (PID) and give us back control of the console.

blender -b test.blend -o results/test####.png -f 1 > test.log 2>&1 & echo $! >> test.pid

Blender now creates 2 files immediately after startup, one containing the progress and one with its PID. Both files can be read with server-side script languages like PHP, Perl or Python.

Important: place the log and PID files into a server readable directory, for example the same as your .blend file. For readability this has been omitted from the code above.

You can start Blender multiple times, but keep in mind that every instance of Blender should write to its own log file and PID file.

Making a PHP script to start Blender and call this script from a browser is quite simple.

exec() allows us to call shell / console commands from PHP.

<?php
exec('blender -b test.blend -o results/test####.png -f 1 > test.log 2>&1 & echo $! >> test.pid');
echo "Maybe this worked.";
?>

Save this as “index.php” in the project directory.

However, getting your computer to execute this command takes 2 more steps.

First: copy your Blender application directory (or on OS X: blender.app) to the same directory where your .blend is.

Second: files, directories and programs have owners (user and group) and rights. The webserver, from which we are trying to start programs, is running under its own user and group called “_www” (in most cases).

We won’t get permission to run Blender unless we give everything to this user and group. You can do this by running this command:

chown -R _www:_www blender/

Or on OS X:

chown -R _www:_www blender.app/

Also make sure all textures, HDRIs and the results dir also belong to _www:

chown -R _www:_www metal_plate_1k_png
chown _www:_www green_point_park_2k.hdr
chown _www:_www results

Here’s what my setup on OS X looks like:

Documents in this case is in /Library/WebServer, not your personal Documents directory. It is the root dir of the webserver.

You can now open http://127.0.0.1/blender in the browser.

The message “Maybe this worked.” should appear and the CPU should be working on a new image now.

Tip for OS X users: if you want to go back to the console, you may get a message “Permission denied” when trying to start Blender. That’s because you don’t have the rights anymore. Use “sudo“ before your Blender command:

sudo ./blender.app/Contents/MacOS/blender -b test.blend -o results/test##.png -f 1

To find out for sure if Blender is actually running, we have multiple options:

  1. In the console: “top” lists all processes and we should see Blender at the top hogging the CPU
  2. Wait until you think it should be finished and go look for an image called “test0001.png”
  3. Open test.log and look for lines starting with “Fra: 1….”
  4. Open test.pid, read the PID and then read output from this command

pgrep blender

Option 1 can be misleading - what if there are more than one instances of Blender running? Also this is a bit messy to do in PHP.

Option 2 is kind of stupid. So that leaves us with 3 and 4, which is what we will use, but in reverse order.

We’re going to add some logic to our script now

Here is a basic outline:

  1. Is Blender running?
    1. Yes: display render progress
    2. No: do we want to render?
      1. Yes: start rendering
      2. No: display link to results,
        display last log entries
        display button to start again

Translated to code:

<h1>Remote Blender</h1>
<?php
$pid = 0;  //initialisation
$pids = [];
 
if(file_exists('test.pid')) $pid = intval(file_get_contents('test.pid')); //load test.pid
 
exec('pgrep blender',$pids); //get all blender processes
 
// 1.
if($pid && in_array($pid,$pids)) { // 1.1
echo "Blender is running!";
//TODO: display render progress
}
else { // 1.2
	if(isset($_POST["command"]) && $_POST["command"]=="start render"){ // 1.2.1
		@unlink("test.log"); //clean up
		@unlink("test.pid");
		echo "attempting to start render...";
		exec('./Blender.app/Contents/MacOS/blender -b test.blend -o results/test####.png -f 1 > test.log 2>&1 & echo $! >> test.pid');
		
	}
	else {
		$results = glob('results/*'); //load all result files
		if(count($results)>0){
			echo "<h3>Results</h3><ul>";
			foreach($results as $file) echo "<li><a href=\"$file\">$file</a></li>";
			echo "</ul>";
		}
		//load the last 300 chars from the log
		exec("tail -c 300 test.log",$messages);
		echo "<h3>Last log entries</h3><textarea cols=100 rows=8>".implode("\n",$messages)."</textarea>";
		echo '<br><br><form method="post"><input name="command" type="submit" value="start render"></form>';
	}
}
 
?>

Manipulate textures

Our interaction with Blender via browser is rather dull at the moment. We can start it and download results and see error messages in the log. But by using PHP’s image manipulation libraries we could add new data to our scene before rendering, e.g., by adding a custom line of text to the metal maps. We can also use custom fonts in TTF or OTF format, if you place them in the project dir.

Before you change the textures, make a copy of all maps and place them in a directory called “master”.

We will test our code first in a separate PHP file:

<?php
$img = new Imagick();
$img->readImage('master/metal_plate_diff_1k.png');
$draw = new ImagickDraw();
$draw->setFont("Torsten.ttf");
$draw->setFontSize(250);
$draw->setFillColor('#00FF00');
$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
$img->annotateImage($draw,1024/2,1024/2,-30,"Graffiti");
$img->writeImage("metal_plate_1k_png/metal_plate_diff_1k.png");
?><img src="metal_plate_1k_png/metal_plate_diff_1k.png">

And save this as “image.php”.

If you don’t have ImageMagick installed on your local system, I recommend this guide on stackoverflow.

It is also possible to manipulate images with the built-in library GD. But I prefer ImageMagick.

If we open http://127.0.0.1/blender/image.php we will see this image

Combining everything

Right now the inserted text is fixed in code, but with some HTML we can add a text input just above our render button to change the text to whatever we want, right before rendering by reading the input of $_POST["text"].

<h1>Remote Blender</h1>
<?php
$pid = 0;  //initialisation
$pids = [];
if(file_exists('test.pid')) $pid = intval(file_get_contents('test.pid')); //load test.pid
exec('pgrep blender',$pids); //get all blender processes
 
if($pid && in_array($pid,$pids)) {
	echo "Blender is running!<br>";
	//TODO: display render progress
} else {
	if(isset($_POST["command"]) && $_POST["command"]=="start render"){
		@unlink("test.log"); //clean up
		@unlink("test.pid");
		
		if(isset($_POST["text"]) && $_POST["text"]!="") {
			//change texture
			$img = new Imagick();
			$img->readImage('master/metal_plate_diff_1k.png');
			$draw = new ImagickDraw();
			$draw->setFont("Torsten.ttf");
			$draw->setFontSize(250);
			$draw->setFillColor('#00FF00');
			$draw->setTextAlignment(\Imagick::ALIGN_CENTER);
			$img->annotateImage($draw,1024/2,1024/2,30,$_POST["text"]);
			$img->writeImage("metal_plate_1k_png/metal_plate_diff_1k.png");
		}
		echo "attempting to start render...";
		exec('./Blender.app/Contents/MacOS/blender -b test.blend -o results/test####.png -f 1 > test.log 2>&1 & echo $! >> test.pid');
	}
	else {
		$results = glob('results/*'); //load all result files
		if(count($results)>0){
			echo "<h3>Results</h3><ul>";
			foreach($results as $file) echo "<li><a href=\"$file\">$file</a></li>";
			echo "</ul>";
		}
		//load the last 300 chars from the log
		exec("tail -c 300 test.log",$messages);
		echo "<h3>Last log entries</h3><textarea cols=100 rows=8>".implode("\n",$messages)."</textarea>";
		echo '<br><br><form method="post">';
		echo 'Text <input name="text" maxlength=20><br>';
		echo '<input name="command" type="submit" value="start render"></form>';
	}
}
?>

...et voilà!

The font used in this image is my own font, just replace it with any TTF or OTF font file you have.

It is just as easy to change the roughness map or bump map or change the color of the whole map, add a new image on top etc. Your imagination is the limit.

One final step

Two things are still missing: render progress and automatic reloads. Progress for one frame is calculated by parsing the log file for rendered tiles and dividing “rendered” by “to render”. Progress for an animation by parsing for the frame number and dividing them by the total number of frames. Unfortunately I don’t know how to get the total number of frames from a .blend file, so we have to know that number and put it in code.

Here is the code to parse progress for one frame

exec("tail -c 300 test.log",$logcontent);
preg_match_all("/Rendered (\d+)\/(\d+) Tiles/",implode("",$logcontent),$found);
$rendered = intval($found[1][0]);
$to_render = intval($found[2][0]);
$progress = round($rendered / $to_render * 100);

We can display the number directly or use it in CSS to style a progress bar:

echo "$progress% completed";
echo '<div style="border:1px solid #aaa;padding:3px;"><div style="background:#aadd33;width:'.$progress.'%;height:50px;"></div></div>';

To see this bar update itself we also need to reload every few seconds:

echo "<script>setTimeout('document.location.reload();',2000);</script>";

Place these lines instead of the placeholder

//TODO: display render progress

The same reload command can be placed right after “exec(‘blender….” so that we see the progress bar immediately.

That’s it!

Download the complete script here.

(Disclaimer: use the code at your own risk! This is development code for demonstration, not production ready code!)

Where to go from here

  • Of course, up to this point you only tipped your toe into the ocean. There is so much more you can do now:
  • You can add upload functionality and upload your .blend directly from the browser. The same for all textures.
  • Display time and date, file_size for resulting images
  • Imagine loading webcam pictures and placing them in your scene.
  • A function to stop Blender while it is running would also be handy.
  • Instead of a simple text font you could use a font with symbols.
  • You can invoke custom Python scripts to change objects in your scene before rendering.
  • Or extend the script to act as a service / slave to be controlled from a central master server, then start multiple instances of that slave, basically creating your own render farm.
  • Use “ffmpeg” to assemble all frames after rendering in the same manner we used Blender
  • Send yourself an email once rendering has finished

So, I hope you were able to follow me through this tutorial. If you’re not a regular coder you may be a bit overwhelmed by everything. Just keep in mind, you’re not the only one with the same questions. Everything used in this tutorial is well known and documented on the internet. It is all a matter of patience and putting it together and searching on Stackoverflow.

About the Author

Torsten Dudai, 38, freelancing programmer, designer, allrounder-problem-solver, living and working in Berlin. Started with Cinema 4D back in 1994 on an Amiga 1200, but switched to Blender 2 years ago and never looked back! My commercial work is mostly product visualisation, which you can find here. I’m also co-organizer of the Berlin Blender Meetup: if you’re in Berlin and interested in Blender, you’re welcome to join.

About the Author

Abby Crawford

I've been a part of the BlenderNation team since 2018, producing Behind the Scenes and Meet the Artist features that highlight Blender artists and their work.

8 Comments

  1. Very nice tutorial! It gives a different perspective how technologies could be used when combined. Thanks a lot!

  2. Nice project. I've also used Blender running in headless mode as a web app.

    As you mention, you can use the command line to start Blender and run a Python script with parameters. That is very powerful. I ran a project last year where 3d printable objects were output in response to user input via a web page. I fed these into a 3d printing facility to manufacture the objects for a public exhibition. All powered by Blender and a bit of coding.

  3. I went an easier route and just opened an aiohttp webserver inside a blender script, to get a REST interface.

    Also you can manage such instances with a process manager like supervisor, systemd or even docker.

  4. Yeah...I've made something similar for "Modular Sofa Configurator" with combination of blender python + php + html/jquery.

  5. Hello, This is the kind of thing I've been looking for but I know nothing about coding.
    What would it cost to for someone to do something like this for me?

Leave A Reply

To add a profile picture to your message, register your email address with Gravatar.com. To protect your email address, create an account on BlenderNation and log in when posting a message.

Advertisement

×