Rendering OpenAI Gym Envs on Binder and Google Colab | by David R. Pugh | Towards Data Science

I am currently using my COVID-19 imposed quarantine to expand my deep learning skills by completing the Deep Reinforcement Learning Nanodegree from Udacity. Almost immediately I ran into the tedious problem of getting my simulations to render properly when training on remote servers.
In particular, getting OpenAI Gym environments to render properly in remote servers such as those supporting popular free compute facilities such as Google Colab and Binder turned out to be more challenging than I expected. In this post, I lay out my solution in the hopes that I might save others time and effort to work it out independently.
If you wish to use Google Colab, then this section is for you! Otherwise, you can skip to the next section for the Binder Preamble.
First, you will need to install the necessary X11 dependencies, in particular Xvfb, which is an X server that can run on machines with no display hardware and no physical input devices. You can install system dependencies inside your Colab notebook by prepending the install command with an exclamation mark (!)which will run the command inside its own Bash shell.
!apt-get install -y xvfb x11-utils
Now that you have installed Xvfb, you need to install a Python wrapper pyvirtualdisplay in order to interact with Xvfb virtual displays from within Python. You also need to install the Python bindings for OpenGL: PyOpenGL and PyOpenGL-accelerate. The former is the actual Python bindings, the latter is an optional set of C (Cython) extensions providing acceleration of common operations in PyOpenGL 3.x.
!pip install pyvirtualdisplay==0.2.* \             PyOpenGL==3.1.* \             PyOpenGL-accelerate==3.1.*
Next, you need to install the OpenAI Gym package. Note that depending on which Gym environment you are interested in working with you may need to add additional dependencies. Since I am going to simulate the LunarLander-v2 environment in my demo below I need to install the box2d extra which enables Gym environments that depend on the Box2D physics simulator.
!pip install gym[box2d]==0.17.*
For simplicity, I have gathered all the software installation steps into a single code block that you can cut and paste into your notebook.
%%bash# install required system dependenciesapt-get install -y xvfb x11-utils# install required python dependenciespip install gym[box2d]==0.17.* \            pyvirtualdisplay==0.2.* \            PyOpenGL==3.1.* \            PyOpenGL-accelerate==3.1.*
Now that all the required software is installed you are ready to create a virtual display (i.e., a display that runs in the background) which the OpenAI Gym Envs can connect to for rendering purposes. You can actually check that there is no display at present by confirming that the value of the DISPLAY environment variable has not yet been set.
!echo $DISPLAY
The code in the cell below creates a virtual display in the background that your Gym Envs can connect to for rendering. You can adjust the size of the virtual buffer as you like but you must set visible=False when working with Xvfb.
This code only needs to be run once in your notebook to start the display.
After running the code above in your notebook you can echo out the value of the DISPLAY environment variable again to confirm that you now have a display running.
!echo $DISPLAY # should now be set to some value
For convenience, I have gathered the above steps into two cells that you can copy and paste into the top of you Google Colab notebooks.
If you wish to use Binder, then this section is for you!
Unlike Google Colab, with Binder you can bake all the required dependencies (including the X11 system dependencies!) into the Docker image on which the Binder instance is based using Binder config files. These config files can either live in the root directory of your Git repo or in a binder sub-directory (my preferred choice).
The first config file that needs to be defined is the apt.txt file which is used to install system dependencies. You can just create an appropriately named file and then list the dependencies you want to install (one per line). After a bit of trial and error, I hit on the following winning combination.
freeglut3-devxvfbx11-utils
The second and config file is the standard environment.yml file used to define a Conda environment. If you are unfamiliar with Conda, then I suggest that you check out my recent articles on Getting started with Conda and Managing project-specific environments with Conda.
name: null channels:  - conda-forge  - defaults dependencies:  - gym-box2d=0.17  - jupyterlab=2.0  - matplotlib=3.2  - pip=20.0  - pip:    - -r file:requirements.txt  - python=3.7  - pyvirtualdisplay=0.2
The final required config file is the requirements.txt file used by Conda to install any additional Python dependencies that are not available via Conda channels using pip.
PyOpenGL==3.1.*PyOpenGL-accelerate==3.1.*
If you are interested in learning more about Binder, then check out the documentation for BinderHub which is the underlying technology behind the Binder project.
Next, you need to create a virtual display in the background which the Gym Envs can connect to for rendering purposes. You can check that there is no display at present by confirming that the value of the DISPLAY environment variable has not yet been set.
!echo $DISPLAY
The code in the cell below creates a virtual display in the background that your Gym Envs can connect to for rendering. You can adjust the size of the virtual buffer as you like but you must set visible=False when working with Xvfb.
This code only needs to be run once per session to start the display.
After running the cell above you can echo out the value of the DISPLAY environment variable again to confirm that you now have a display running.
!echo $DISPLAY
Just to prove that the above setup works as advertised I will run a short simulation. First I define an Agent that chooses an action randomly from the set of possible actions and then define a function that can be used to create such agents. Then I wrap up the code to simulate a single episode of an OpenAI Gym environment. Note that the implementation assumes that the provided environment supports rgb_array rendering (which not all Gym environments support!).
Currently there appears to be a non-trivial amount of flickering during the simulation. Not entirely sure what is causing this undesirable behavior. If you have any idea how to improve this, please leave a comment below. I will be sure to update this post accordingly if I find a good fix.