diff --git a/README.md b/README.md
index 44518888c9e91f433a3d3f74a4c38133d5c1bf37..78564f976a84ede5c1545670172da6563d471a82 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,15 @@
-![PULT logo](www/pult/pult-logo-192x74.png)
+![ PULT logo ](www/pult/pult-logo-192x74.png)
 
 PULT is the ULTimate Learning/Teaching Tool
 ===========================================
 
 Contents
 --------
- * What is PULT?
- * Okay, more technically, what is PULT?
- * How does this look like in practice?
- * License  
-
-Installation: see INSTALL.md (to be written soon)
+ * [ What is PULT?                         ](#what-is-pult)
+ * [ Okay, more technically, what is PULT? ](#okay-more-technically-what-is-pult)
+ * [ How does this look like in practice?  ](#how-does-this-look-like-in-practice)
+ * [ Licence                               ](#licence)
+ * [ Installation                          ](#installation)
 
 What is PULT?
 -------------
@@ -37,7 +36,7 @@ What is PULT?
    You can run it on your own server for best-possible data
    protection and privacy.  You can study how it works, adapt
    it to your needs, and publish your improvements for everyone
-   to benefit.  Also, there are no licensing fees.
+   to benefit.  Also, there are no licencing fees.
 
  * **PULT husbands your CPU power and network bandwidth.**
 
@@ -61,15 +60,15 @@ Okay, more technically, what is PULT?
 -------------------------------------
 PULT is a collection of small programs, mostly Unix shell scripts.
 It provides a console (in German: „Pult“:–) where the lecturer can
-select which visual content is offered to the audioence via a web
+select which visual content is offered to the audience via a web
 interface, *noVNC*.  These visual contents can be a shared desktop,
 a camera view, an uploaded image or screenshot, or some arbitrary
 content provided by some other tool via a web interface, for
 instance a BigBlueButton presentation or the shared whiteboard of
 OpenMeetings.
 
-PULT itself only handles video.  For the audio part and chat part
-it cooperates with Mumble.
+PULT itself only handles video.  For the audio part and the chat
+part it cooperates with Mumble.
 
 The virtual “meeting rooms” in PULT are called *channels*.  For each
 channel, there is one instance of noVNC, showing a virtual desktop,
@@ -147,14 +146,14 @@ rather than full-screen.  For camera modules this is the default.
 A typical application is to run a VNC shared desktop full-screen
 and to show a camera view of the lecturer at the edge. 
 
-> ![A minimalistic PULT console](doc/images/pult-channel-1-20201226-153714.png)
+> ![ A minimalistic PULT console ](doc/images/pult-channel-1-20201226-153714.png)
 >
 > Figure 1: A minimalistic PULT console.
 >
 > VNC is enabled automatically.
 > The buttons are for switching a camera.
 
-> ![A PULT console with many features enabled](doc/images/pult-channel-6-20201226-154114.png)
+> ![ A PULT console with many features enabled ](doc/images/pult-channel-6-20201226-154114.png)
 >
 > Figure 2: A PULT console with many features enabled
 >
@@ -178,7 +177,7 @@ How does this look like in practice?
    go to the web page of the channel, type in the channel's password,
    and see a welcome screen.
 
-> ![Welcome screen](doc/images/novnc-welcome-screen-20201226-135612.png)
+> ![ Welcome screen ](doc/images/novnc-welcome-screen-20201226-135612.png)
 >
 > The welcome screen at the Bochum University of Applied Sciences
 
@@ -190,11 +189,11 @@ How does this look like in practice?
    camera.  The camera view becomes visible in the lower right
    corner of the channel.
 
-> ![PULT console with VNC and Camera started](doc/images/pult-vnc-and-camera-started-20201226-135647.png)
+> ![ PULT console with VNC and Camera started ](doc/images/pult-vnc-and-camera-started-20201226-135647.png)
 >
 > PULT console with VNC and Camera started
 
-> ![Shared desktop plus camera view](doc/images/novnc-desktop-janus-camera-20201226-180026.png)
+> ![ Shared desktop plus camera view ](doc/images/novnc-desktop-janus-camera-20201226-180026.png)
 >
 > The noVNC screen shows the lecturer's shared desktop in
 > full-screen mode and the camera view in the lower right corner.
@@ -205,7 +204,7 @@ How does this look like in practice?
    the shared desktop.  After the welcoming, L presses “Camera min”
    to switch back to the previous screen arrangement.
 
-> ![Camera full-screen](doc/images/janus-camera-full-screen-20201226-135814.png)
+> ![ Camera full-screen ](doc/images/janus-camera-full-screen-20201226-135814.png)
 >
 > The camera in full-screen view
 
@@ -215,7 +214,7 @@ How does this look like in practice?
    L minimises the presentation on the desktop and presents the
    source code.
 
-> ![Shared desktop plus camera view](doc/images/novnc-desktop-and-janus-camera-20201226-135830.png)
+> ![ Shared desktop plus camera view ](doc/images/novnc-desktop-and-janus-camera-20201226-135830.png)
 >
 > The same arrangement with the desktop showing a smaller view of
 > the presentation on the right and a source code example on the
@@ -240,7 +239,7 @@ How does this look like in practice?
    S2 logs into OpenMeetings and draws the mathematical calculations
    for everyone to see via noVNC.
 
-> ![Shared OpenMeetigns whiteboard plus camera view](doc/images/novnc-om-board-and-janus-camera-20201226-135905.png)
+> ![ Shared OpenMeetigns whiteboard plus camera view ](doc/images/novnc-om-board-and-janus-camera-20201226-135905.png)
 >
 > A shared OpenMeetings whiteboard presented via noVNC.
 > The cameraa view still shows the lecturer.
@@ -250,7 +249,7 @@ How does this look like in practice?
    S2 logs into Jitsi Meet, and the second camera view appears
    on the noVNC screen.
 
-> ![Shared OpenMeetings whiteboard puls two camera views](doc/images/novnc-om-board-janus-camera-and-novnc-camera-20201226-140306.png)
+> ![ Shared OpenMeetings whiteboard puls two camera views ](doc/images/novnc-om-board-janus-camera-and-novnc-camera-20201226-140306.png)
 > 
 > A shared OpenMeetings whiteboard presented via noVNC, plus
 > two camera views. The upper one was sent via Jitsi Meet.
@@ -270,12 +269,12 @@ How does this look like in practice?
    and “VNC stop”.  The channel shows a welcome screen again.
    To end the session, everyone closes their browser windows.
 
-License
+Licence
 -------
 
 PULT is Free Software (Open Source) under the
-[GNU Affero General Public License (AGPL)](https://www.gnu.org/licenses/agpl-3.0.html),
-version 3 or higher, 
+[ GNU Affero General Public License (AGPL) ](https://www.gnu.org/licenses/agpl-3.0.html),
+either version 3 of the licence, or, at your option, any later version.
 
 In short this means:
 
@@ -286,19 +285,200 @@ In short this means:
    
    To make this possible, you have, as a user of the software,
    the right to obtain its source code, *even if the software
-   runs on a server*, and you only connect to that server.
+   runs on a server, and you only connect to that server*.
    (This “even on a server” clause is what distinguishes the
-   GNU Affero General Public License (AGPL) from the “normal”
-   GNU General Public Licnese (GPL).)
+   GNU Affero GPL from the “normal” GNU GNU GPL.)
 
- * You are free to distribute copies of this software,
-   gratis or for a fee, *provided that* you don't deny
-   anyone the rights we gave to you.
+ * You are free to redistribute copies of this software,
+   gratis or for a fee, *provided that you don't deny
+   anyone the rights we gave to you*.
    (This “provided that” clause is the *Copyleft*
-   principle, an essential feature of the GNU licenses.)
+   principle, an essential feature of the GNU licences.)
    
- * You are free to improve this software and to publish your
-   improvements, so the whole society benefits from it.
+ * You are free to improve this software and to redistribute
+   your improvements, so everyone can benefit from it.
 
    Again, you may do this as a charity service, but you are also
    free to found a business providing this kind of service.
+
+ * There is NO WARRANTY, unless you are paying someone
+   to provide a warranty for you.
+
+The GNU AGPL applies to PULT itself.  Many components used by PULT
+are under different licences.  However they are all Free Software,
+and their licence conditions are essentially subsets of those of
+the GNU AGPL.
+
+A simplified list of the individual licences follows.
+For details, please examine the individual software packages.
+
+ * *Mumble* (server and client): 3-clause BSD licence
+
+ * Mumble web interface *mumble-web*: ISC licence
+
+ * Web server: *nginx*: 2-clause BSD licence,
+   or *Apache*: Apache licence 2.0
+
+ * *noVNC*: MPL 2.0
+
+ * *yesVNC*: GNU APGL 3+ (part of PULT)
+
+ * *websockify*: GNU GPL 3
+
+ * *TigerVNC*: GNU GPL 2.0
+
+ * *TightVNC*: GNU GPL 2.0+
+
+ * *x11vnc*: GNU GPL 2.0
+
+ * *ssvnc*: GNU GPL 2.0
+
+ * *OpenSSH*: 3-clause BSD licence
+
+ * *PuTTY*: MIT/Expat licence
+
+ * *Janus WebRTC Gateway*: GNU GPL 3
+
+ * *Firefox* web browser and *geckodriver*: MPL 2.0
+
+ * *Chromium* web browser and *chromedriver*: 3-clause BSD licence
+
+ * *Selenium*: Apache licence 2.0
+
+ * *Python*: PSF licence
+
+ * *Node.js*: MIT/Expat licence
+
+ * *Netcat*: 3-clause BSD licence
+
+ * *Bash*: GNU GPL 3+
+
+ * *botamusique*: MIT/Expat licence
+
+For an overview about the licences themselves, see
+[ https://www.gnu.org/licenses/license-list.html ](https://www.gnu.org/licenses/license-list.html).
+
+Installation
+------------
+
+You can find detailed installation instructions in the file
+[ doc/pult-install.pdf ](doc/pult-install.pdf) (to be written).
+This section gives a brief overview.
+
+The PULT server was desiged to run on GNU/Linux.  However it should
+not be difficult to make it run on other Unix-like systems.
+
+PULT consists of several parts to be installed separately.  You can
+choose which ones you want to use.  Most of them make use of other
+software which is well-established and probably included in your
+GNU/Linux distribution for easy installation.
+
+ * **Mumble**
+
+   PULT itself only handles video.  For a useful system you need
+   something which handles the audio part.  We highly recommend
+   Mumble for its stability and quality.  Mumble also provides a
+   chat function which has proven very useful.
+
+   PULT comes with some utilites which are designed to cooperate
+   with Mumble.
+
+   For the audience, the most efficient way to participate in Mumble
+   is to install a Mumble client.  If you want to provide an easier,
+   albeit less efficient access method, you can install mumble-web,
+   a web interface for Mumble.
+
+   The Mumble server (Murmur) is included in many GNU/Linux
+   distributions.  The Mumble client (for the audience) for
+   vaious platforms can be downloaded from
+   [ https://www.mumble.com/ ](https://www.mumble.com/).
+   The Mumble web interface is available at
+   [ https://github.com/Johni0702/mumble-web ](https://github.com/Johni0702/mumble-web).
+
+ * **VNC**
+ 
+   Almost everything the audience can watch is conveyed via noVNC.
+
+   For sharing a desktop via VNC, the most efficient way is to
+   install a VNC server on the lecturer's computer.  If you want
+   to provide an easier, albeit less efficient method, you can
+   install yesVNC, a web interface for VNC, developed as part of
+   PULT.
+
+   The VNC server and client software, including the noVNC web
+   interface, is included in many GNU/Linux distributions.
+   Lecturers who want to install a VNC server can use TightVNC,
+   [ https://tightvnc.com/download.php ](https://tightvnc.com/download.php).
+   Lecturers who want to use an SSH tunnel but cannot easily use
+   OpenSSH, can use PuTTY, available at
+   [ https://www.chiark.greenend.org.uk/~sgtatham/putty/ ](https://www.chiark.greenend.org.uk/~sgtatham/putty/).
+
+ * **PULT Itself**
+
+   PULT itself is a collection of small programs, mostly shell
+   scripts, to control everything.  This includes start/stop scripts
+   for systemd, HTML files and CGI scripts for web interfaces, and
+   configuration templates for your web server (*Apache* or *nginx*).
+
+   There is a shell script, *pult-install*, in the same directory as
+   this file.  When you run it with root privileges, it will copy
+   the files which make up PULT to their directories and give them
+   appropriate permissions.  However, this script is incomplete,
+   leaving important tasks such as creating user accounts and
+   enabling systemd services as manual tasks.
+
+ * **CVH-Camera**
+ 
+   We have developed a native camera subsystem for PULT, CVH-Camera.
+   It provides a simple web interface to share a camera view and
+   uses the Janus WebRTC gateway to distribute it to the audience.
+   That way, CVH-Camera provides high quality at resonably low
+   consumption of CPU power and network bandwidth.
+
+   CVH-Camera is available at
+   [ https://gitlab.cvh-server.de/sdoering/cvh-camera ](https://gitlab.cvh-server.de/sdoering/cvh-camera).
+   Parts of it are included in the PULT sources and get installed
+   via the *pult-install* shell script.
+
+ * **Browser-Based Video Contents**
+
+   PULT can use Selenium, Python, and the web browsers Chromium and
+   Firefox for accessing web contents provided by other systems.
+
+   In particular, PULT can extract a camera view out of an external
+   Jitsi Meet or BigBlueButton, as an alternative to CVH-Camera.
+   The browser method requires more CPU power and network bandwidth
+   at a lower quality, but it provides the same level of reliability
+   as CVH-Camera.
+
+   The required software, Selenium, Python, Chromium, ChromeDriver,
+   and Firefox, is included in most GNU/Linux distributions.
+   One additional  component, GecokDriver, can be downloaded from
+   [ https://github.com/mozilla/geckodriver/releases ](https://github.com/mozilla/geckodriver/releases).
+
+ * **External Conference Systems**
+
+   If you want to use a shared whiteboard, we recommend to
+   install OpenMeetings and to import its whiteboard into
+   PULT as a browser-based content (see above).  The same
+   is possible for BigBlueButton.
+   
+   It is straightforward to import contents from other external
+   systems.  Just copy and modify one of the existing modules,
+   for instance *bbb-camera*.
+
+ * **Other Useful Stuff**
+
+   We also provide *CVH-Bot*, a bot which joins the Mumble chat and
+   makes it easy to conduct polls.  CVH-Bot is available at
+   [ https://gitlab.cvh-server.de/jvongehr/cvh-bot ](https://gitlab.cvh-server.de/jvongehr/cvh-bot).
+
+   Speaking of Mumble bots, we can recommend *botamusique* for
+   playing music in Mumble – for your events, for fun and for
+   testing purposes.  You can find botamusique at
+   [ https://github.com/azlux/botamusique ](https://github.com/azlux/botamusique).
+
+   At the Bochum University of Applied Sciences, we provide a web
+   page which allows for quick access to a *virtual room*, i.e. a
+   combination of a Mumble channel and a VNC channel in PULT. Be
+   welcome to copy it and to adapt it to your room structure.
diff --git a/home/pult/vnc-testbild.pdf b/home/pult/vnc-testbild.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..4d4a22152ffb288b73df368192c74cfd92c2f371
Binary files /dev/null and b/home/pult/vnc-testbild.pdf differ
diff --git a/home/pult/vncX/.vnc/passwd b/home/pult/vncX/.vnc/passwd
new file mode 100644
index 0000000000000000000000000000000000000000..cc48b750afa083ee7b47c30fc859199d7281b835
--- /dev/null
+++ b/home/pult/vncX/.vnc/passwd
@@ -0,0 +1,2 @@
+�!�<�Kp;3�
+'�M�
\ No newline at end of file
diff --git a/home/pult/vncX/.vnc/passwdfile b/home/pult/vncX/.vnc/passwdfile
new file mode 100644
index 0000000000000000000000000000000000000000..acca0cce730eb02f88f6771092106b7660dc4150
--- /dev/null
+++ b/home/pult/vncX/.vnc/passwdfile
@@ -0,0 +1,2 @@
+topsecret
+viewonly
diff --git a/home/pult/vncX/.vnc/xstartup b/home/pult/vncX/.vnc/xstartup
new file mode 100755
index 0000000000000000000000000000000000000000..ef06468025a05acb7ebbfaa5e1a3b54a85ff5a65
--- /dev/null
+++ b/home/pult/vncX/.vnc/xstartup
@@ -0,0 +1,4 @@
+#!/bin/dash
+
+xsetroot -solid white
+xpdf -fullscreen "$HOME/vnc-testbild.pdf" 6
diff --git a/home/pult/vncX/vnc-testbild.pdf b/home/pult/vncX/vnc-testbild.pdf
new file mode 120000
index 0000000000000000000000000000000000000000..c5b4e47eda58b83fe1e3d4baa840cba35494cdd0
--- /dev/null
+++ b/home/pult/vncX/vnc-testbild.pdf
@@ -0,0 +1 @@
+../vnc-testbild.pdf
\ No newline at end of file
diff --git a/pult-install b/pult-install
index 3bf80e361b7ef8eaeaf485c5046f138bfb4a0169..7c9ce5f3e2764573facc793a9e89677f00539eb6 100755
--- a/pult-install
+++ b/pult-install
@@ -85,6 +85,13 @@ Please install manually:
  - etc/nginx/sites-available/fragment.conf  in your nginx configuration
  - etc/janus/janus.plugin.videoroom.jcfg.fragment  in your janus configuration
  - www/vnc  as your VNC channel web page
+ - www/virtual-rooms  as your quick access interface
+ - home/pult  as the home directory for your "pult" user
+ - home/pult/vncX  as the VNC home directories inside /home/pult
+   Note: It contains a subdirectory ".vnc".
+   Replace X by the channel numbers: vnc1, vnc2, ...
+   The file ".vnc/passwdfile" is plain text.  When you change it,
+   use "cat passwdfile | vncpasswd -f > passwd" to regenerate a "passwd" file.
  - adapt cvh-camera-update-credentials to your www directory,
    and add a cron job to execute it periodically, for instance:
    42 * * * * $sbin_directory/cvh-camera-update-credentials
diff --git a/www/virtual-rooms/conference.html b/www/virtual-rooms/conference.html
new file mode 100644
index 0000000000000000000000000000000000000000..ad8729d2c2f1652fcdb5f70da1eb65159a391c19
--- /dev/null
+++ b/www/virtual-rooms/conference.html
@@ -0,0 +1,104 @@
+<!doctype html public "-//W3C//DTD HTML 4.0 Strict//EN">
+<!--
+  Web Interface for Virtual Rooms at Campus Velbert/Heiligenhaus,
+  Bochum University of Applied Sciences
+
+  Copyright 2020  Peter Gerwinski <http://www.peter.gerwinski.de>
+
+  This program is free software: you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation, either version 3 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf8">
+    <title>Virtuelle Räume</title>
+    <link rel="stylesheet" href="stylesheets/default.css" type="text/css">
+  </head>
+  <body>
+    <img src="logo-hochschule-bochum-cvh-vertikal-v2-128x205.png"
+         alt="Hochschule Bochum, Campus Velbert/Heiligenhaus"
+         class="logo" align="right"/>
+    <p align="right"><small><a href="index.en.html">[more options]<a></small></p>
+    <h1>Welcome to our Conference!</h1>
+    <noscript>
+      <p>
+        <strong>Please note:</strong>
+        These web interfaces only work with cookies and JavaScript.
+        Sorry for that.
+      </p>
+    </noscript>
+    <script type="text/javascript">
+
+      function generateUserName ()
+      {
+        var userName = document.getElementById ("user_name");
+        var length = 5;
+        var result = 'User_';
+        var characters = '0123456789';
+        var charactersLength = characters.length;
+        for (var i = 0; i < length; i++)
+          result += characters.charAt (Math.floor (Math.random () * charactersLength));
+        userName.value = result;
+      }
+
+      function enterRoom ()
+      {
+        var userName = document.getElementById ("user_name");
+        userName.value = userName.value.replace (/ /g, "_");
+        var url = new URL (window.location.href);
+        var vncChannel = url.searchParams.get ("ch");
+        var password = url.searchParams.get ("pw");
+        var mumbleURL = "https://mumble.cvh-server.de"
+                        + "?channelName=Conference%20Room"
+                        + "&username=" + userName.value
+                        + "&matrix=true";
+        if (!vncChannel)
+          alert ("Room not selected. The link to this page appears to be broken.");
+        else if (!password)
+          alert ("Missing password. The link to this page appears to be broken.");
+        else if (!userName.value)
+          alert ("Missing username. If you want to participate anonymously with a random name, press the “Generate” button.");
+        else if (vncChannel > 0)
+          {
+            var halfResolution = document.getElementById ("half_resolution");
+            window.open (mumbleURL, "_blank");
+            window.location.href = "https://novnc.cvh-server.de/vnc.html?host=novnc.cvh-server.de&port=443&path=websock/"
+                                   + vncChannel + "/&reconnect=1&resize=scale&autoconnect=true&password=" + password;
+          }
+        else
+          window.location.href = mumbleURL;
+        return false;
+      }
+
+    </script>
+    <p>Please enter a name that will be displayed with your contributions.</p>
+    <form onsubmit="return enterRoom ();">
+      <p>
+        Username:
+        <input type="text" size="20" id="user_name"/>
+        <button type="button" id="random" onclick="generateUserName ();">Generate</button>
+      </p>
+      <p><button type="submit" id="submit">Enter Conference Room</button><p>
+    </form>
+    <p>
+      After pressing the “Enter Conference Room” button you will be forwarded
+      to our video system (VNC), where you can watch the presentation.
+      In addition, a second tab will open and connect you to our audio system (Mumble),
+      where you can listen to the speaker, speak yourself, and exchange text messages.
+    </p>
+    <p>
+      Contact:
+      <a href="mailto:peter.gerwinski@hs-bochum.de">Prof. Dr. Peter Gerwinski</a>
+    </p>
+  </body>
+</html>
diff --git a/www/virtual-rooms/index.de.html b/www/virtual-rooms/index.de.html
new file mode 100644
index 0000000000000000000000000000000000000000..89388a08246d849847601cd04ec8e8c0b91a8338
--- /dev/null
+++ b/www/virtual-rooms/index.de.html
@@ -0,0 +1,266 @@
+<!doctype html public "-//W3C//DTD HTML 4.0 Strict//EN">
+<!--
+  Web Interface for Virtual Rooms at Campus Velbert/Heiligenhaus,
+  Bochum University of Applied Sciences
+
+  Copyright 2020  Peter Gerwinski <http://www.peter.gerwinski.de>
+
+  This program is free software: you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation, either version 3 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf8">
+    <title>Virtuelle Räume</title>
+    <link rel="stylesheet" href="stylesheets/default.css" type="text/css">
+  </head>
+  <body>
+    <img src="logo-hochschule-bochum-cvh-vertikal-v2-128x205.png"
+         alt="Hochschule Bochum, Campus Velbert/Heiligenhaus"
+         class="logo" align="right"/>
+    <p align="right"><small><a href="index.en.html">[English]<a></small></p>
+    <h1>Virtuelle Räume</h1>
+    <noscript>
+      <p>
+        <strong>Hinweis:</strong>
+        Diese Web-Interfaces funktionieren leider nur mit Cookies und JavaScript.
+      </p>
+    </noscript>
+    <script type="text/javascript">
+
+      function getVncChannel ()
+      {
+        var room = document.getElementById ("room");
+        var vncChannel = 0;
+        switch (room.value)
+          {
+            case "--- bitte auswählen ---":
+              vncChannel = -1;
+              break;
+            case "Hörsaal%201":
+            case "Seminarraum%201":
+              vncChannel = 1;
+              break;
+            case "Seminarraum%202":
+            case "Conference%20Room":
+              vncChannel = 6;
+              break;
+            case "Hörsaal%203":
+            case "Seminarraum%203":
+              vncChannel = 3;
+              break;
+            case "Hörsaal%204":
+            case "Seminarraum%204":
+              vncChannel = 4;
+              break;
+          }
+        return vncChannel;
+      }
+
+      function roomChanged ()
+      {
+        var vncChannel = getVncChannel ();
+        if (vncChannel < 0)
+          {
+            document.getElementById ("vnc_only").disabled = true;
+            document.getElementById ("vnc_and_mumble").disabled = true;
+            document.getElementById ("vnc_and_mumble").checked = true;
+            document.getElementById ("mumble_only").disabled = true;
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("user_name").disabled = true;
+            document.getElementById ("user_name_label").style.color = "gray";
+            document.getElementById ("submit").disabled = true;
+          }
+        else if (vncChannel > 0)
+          {
+            document.getElementById ("vnc_only").disabled = false;
+            document.getElementById ("vnc_and_mumble").disabled = false;
+            document.getElementById ("vnc_and_mumble").checked = true;
+            document.getElementById ("mumble_only").disabled = false;
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+        else
+          {
+            document.getElementById ("vnc_only").disabled = true;
+            document.getElementById ("vnc_and_mumble").disabled = true;
+            document.getElementById ("mumble_only").disabled = false;
+            document.getElementById ("mumble_only").checked = true;
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("submit").disabled = false;
+          }
+      }
+
+      function vncOrMumbleChanged ()
+      {
+        var vncOnly = document.getElementById ("vnc_only");
+        var mumbleOnly = document.getElementById ("mumble_only");
+        if (vncOnly.checked)
+          {
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = true;
+            document.getElementById ("user_name_label").style.color = "gray";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+        else if (mumbleOnly.checked)
+          {
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("submit").disabled = false;
+          }
+        else
+          {
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+      }
+
+      function enterRoom ()
+      {
+        var room = document.getElementById ("room");
+        var vncChannel = getVncChannel ();
+        var userName = document.getElementById ("user_name");
+        var mumbleURL;
+        if (document.getElementById ("vnc_only").checked)
+          mumbleURL = "";
+        else
+          mumbleURL = "https://mumble.cvh-server.de"
+                      + "?channelName=" + room.value
+                      + "&username=" + userName.value
+                      + "&matrix=true";
+        if (vncChannel < 0)
+          alert ("Bitte Raum auswählen.");
+        else if (vncChannel == 6)
+          {
+            var room = "100" + vncChannel;
+            var halfResolution = document.getElementById ("half_resolution");
+            var prescale = "";
+            if (halfResolution.checked)
+              {
+                vncChannel += 10;
+                prescale = "&video_prescale=1/2"
+              }
+            if (mumbleURL.length > 0)
+              window.open (mumbleURL, "_blank");
+            window.location.href = "https://streaming.cvh-server.de/vnc.html?host=streaming.cvh-server.de&port=443&path=websock/"
+                                   + vncChannel + "/&reconnect=1&resize=scale&autoconnect=true&password=" + password.value
+                                   + "&room=" + room + prescale;
+          }
+        else if (vncChannel > 0)
+          {
+            var halfResolution = document.getElementById ("half_resolution");
+            if (halfResolution.checked)
+              vncChannel += 6;
+            if (mumbleURL.length > 0)
+              window.open (mumbleURL, "_blank");
+            window.location.href = "https://novnc.cvh-server.de/vnc.html?host=novnc.cvh-server.de&port=443&path=websock/"
+                                   + vncChannel + "/&reconnect=1&resize=scale&autoconnect=true&password=" + password.value;
+          }
+        else
+          window.location.href = mumbleURL;
+        document.getElementById ("clear_room").selected = true;
+        return false;
+      }
+
+    </script>
+    <form onsubmit="return enterRoom ();">
+      Raum:
+      <select name="room" id="room" onchange="roomChanged ();">
+        <option selected="selected" value="none" id="clear_room">--- bitte auswählen ---</option>
+        <option value="Hörsaal%201">Hörsaal 1</option>
+        <option value="Hörsaal%202">Hörsaal 2</option>
+        <option value="Hörsaal%203">Hörsaal 3</option>
+        <option value="Hörsaal%204">Hörsaal 4</option>
+        <option value="Seminarraum%201">Seminarraum 1</option>
+        <option value="Seminarraum%202">Seminarraum 2</option>
+        <option value="Seminarraum%203">Seminarraum 3</option>
+        <option value="Seminarraum%204">Seminarraum 4</option>
+        <option value="Seminarraum%205">Seminarraum 5</option>
+        <option value="Seminarraum%206">Seminarraum 6</option>
+        <option value="Seminarraum%207">Seminarraum 7</option>
+        <option value="Besprechungsraum%201">Besprechungsraum 1</option>
+        <option value="Besprechungsraum%202">Besprechungsraum 2</option>
+        <option value="Raum%201">Raum 1</option>
+        <option value="Raum%202">Raum 2</option>
+        <option value="Raum%203">Raum 3</option>
+        <option value="Raum%204">Raum 4</option>
+        <option value="Raum%205">Raum 5</option>
+        <option value="Raum%206">Raum 6</option>
+        <option value="Conference%20Room">Conference Room</option>
+      </select>
+      <br/>
+      <table border="0" cellpadding="0" cellspacing="30">
+        <tr valign="top">
+          <td>
+            <input type="radio" name="vnc_or_mumble" id="vnc_only" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="vnc_only">nur Bild (Ton separat über Mumble)</label><br/>
+            <input type="radio" name="vnc_or_mumble" id="vnc_and_mumble" checked="checked" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="vnc_and_mumble">Bild und Ton (2 Tabs)</label><br/>
+            <input type="radio" name="vnc_or_mumble" id="mumble_only" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="mumble_only">nur Ton (Mumble-Web-Client)</label>
+          </td>
+          <td>
+            <input type="radio" name="resolution" id="full_resolution" checked="checked" disabled="true"/>
+            <label for="full_resolution">volle Auflösung</label><br/>
+            <input type="radio" name="resolution" id="half_resolution" disabled="true"/>
+            <label for="half_resolution">halbe Auflösung</label><br/>
+          </td>
+        </tr>
+      </table>
+      <table border="0" cellpadding="0" cellspacing="5">
+        <tr>
+          <td id="password_label" style="color: gray;">Passwort für VNC:</td>
+          <td><input type="password" size="25" id="password" disabled="true"/></td>
+        </tr>
+        <tr valign="top">
+          <td id="user_name_label" style="color: gray;">Benutzername für Mumble: &nbsp;</td>
+          <td>
+            <input type="text" size="25" id="user_name" disabled="true"/><br/>
+            <font size="-3">(z.&thinsp;B.&nbsp;pgerwinski für „Peter Gerwinski“)</font>
+          </td>
+        </tr>
+      </table>
+      <br/>
+      <button type="submit" id="submit" disabled="true">Raum betreten</button>
+    </form>
+    <p>
+      Ansprechpartner:
+      <a href="mailto:peter.gerwinski@hs-bochum.de">Prof. Dr. Peter Gerwinski</a>
+    </p>
+  </body>
+</html>
diff --git a/www/virtual-rooms/index.en.html b/www/virtual-rooms/index.en.html
new file mode 100644
index 0000000000000000000000000000000000000000..84a1afee7ef2ec47be41a96619de358e6931dfd9
--- /dev/null
+++ b/www/virtual-rooms/index.en.html
@@ -0,0 +1,265 @@
+<!doctype html public "-//W3C//DTD HTML 4.0 Strict//EN">
+<!--
+  Web Interface for Virtual Rooms at Campus Velbert/Heiligenhaus,
+  Bochum University of Applied Sciences
+
+  Copyright 2020  Peter Gerwinski <http://www.peter.gerwinski.de>
+
+  This program is free software: you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation, either version 3 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <http://www.gnu.org/licenses/>.
+-->
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf8">
+    <title>Virtal Rooms</title>
+    <link rel="stylesheet" href="stylesheets/default.css" type="text/css">
+  </head>
+  <body>
+    <img src="logo-hochschule-bochum-cvh-vertikal-v2-128x205.png"
+         alt="Hochschule Bochum, Campus Velbert/Heiligenhaus"
+         class="logo" align="right"/>
+    <p align="right"><small><a href="index.de.html">[Deutsch]<a></small></p>
+    <h1>Virtual Rooms</h1>
+    <noscript>
+      <p>
+        <strong>Please note:</strong>
+        These web interfaces only work with cookies and JavaScript.
+        Sorry for that.
+      </p>
+    </noscript>
+    <script type="text/javascript">
+
+      function getVncChannel ()
+      {
+        var room = document.getElementById ("room");
+        var vncChannel = 0;
+        switch (room.value)
+          {
+            case "--- please select ---":
+              vncChannel = -1;
+              break;
+            case "Hörsaal%201":
+            case "Seminarraum%201":
+              vncChannel = 1;
+              break;
+            case "Seminarraum%202":
+            case "Conference%20Room":
+              vncChannel = 6;
+              break;
+            case "Hörsaal%203":
+            case "Seminarraum%203":
+              vncChannel = 3;
+              break;
+            case "Hörsaal%204":
+            case "Seminarraum%204":
+              vncChannel = 4;
+              break;
+          }
+        return vncChannel;
+      }
+
+      function roomChanged ()
+      {
+        var vncChannel = getVncChannel ();
+        if (vncChannel < 0)
+          {
+            document.getElementById ("vnc_only").disabled = true;
+            document.getElementById ("vnc_and_mumble").disabled = true;
+            document.getElementById ("vnc_and_mumble").checked = true;
+            document.getElementById ("mumble_only").disabled = true;
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("user_name").disabled = true;
+            document.getElementById ("user_name_label").style.color = "gray";
+            document.getElementById ("submit").disabled = true;
+          }
+        else if (vncChannel > 0)
+          {
+            document.getElementById ("vnc_only").disabled = false;
+            document.getElementById ("vnc_and_mumble").disabled = false;
+            document.getElementById ("vnc_and_mumble").checked = true;
+            document.getElementById ("mumble_only").disabled = false;
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+        else
+          {
+            document.getElementById ("vnc_only").disabled = true;
+            document.getElementById ("vnc_and_mumble").disabled = true;
+            document.getElementById ("mumble_only").disabled = false;
+            document.getElementById ("mumble_only").checked = true;
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("submit").disabled = false;
+          }
+      }
+
+      function vncOrMumbleChanged ()
+      {
+        var vncOnly = document.getElementById ("vnc_only");
+        var mumbleOnly = document.getElementById ("mumble_only");
+        if (vncOnly.checked)
+          {
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = true;
+            document.getElementById ("user_name_label").style.color = "gray";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+        else if (mumbleOnly.checked)
+          {
+            document.getElementById ("full_resolution").disabled = true;
+            document.getElementById ("half_resolution").disabled = true;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = true;
+            document.getElementById ("password_label").style.color = "gray";
+            document.getElementById ("submit").disabled = false;
+          }
+        else
+          {
+            document.getElementById ("full_resolution").disabled = false;
+            document.getElementById ("half_resolution").disabled = false;
+            document.getElementById ("user_name").disabled = false;
+            document.getElementById ("user_name_label").style.color = "black";
+            document.getElementById ("password").disabled = false;
+            document.getElementById ("password_label").style.color = "black";
+            document.getElementById ("submit").disabled = false;
+          }
+      }
+
+      function enterRoom ()
+      {
+        var room = document.getElementById ("room");
+        var vncChannel = getVncChannel ();
+        var userName = document.getElementById ("user_name");
+        var mumbleURL;
+        if (document.getElementById ("vnc_only").checked)
+          mumbleURL = "";
+        else
+          mumbleURL = "https://mumble.cvh-server.de"
+                      + "?channelName=" + room.value
+                      + "&username=" + userName.value
+                      + "&matrix=true";
+        if (vncChannel < 0)
+          alert ("Please select a room.");
+        else if (vncChannel == 6)
+          {
+            var room = "100" + vncChannel;
+            var halfResolution = document.getElementById ("half_resolution");
+            var prescale = "";
+            if (halfResolution.checked)
+              {
+                vncChannel += 10;
+                prescale = "&video_prescale=1/2"
+              }
+            if (mumbleURL.length > 0)
+              window.open (mumbleURL, "_blank");
+            window.location.href = "https://streaming.cvh-server.de/vnc.html?host=streaming.cvh-server.de&port=443&path=websock/"
+                                   + vncChannel + "/&reconnect=1&resize=scale&autoconnect=true&password=" + password.value
+                                   + "&room=" + room + prescale;
+          }
+        else if (vncChannel > 0)
+          {
+            var halfResolution = document.getElementById ("half_resolution");
+            if (halfResolution.checked)
+              vncChannel += 6;
+            if (mumbleURL.length > 0)
+              window.open (mumbleURL, "_blank");
+            window.location.href = "https://novnc.cvh-server.de/vnc.html?host=novnc.cvh-server.de&port=443&path=websock/"
+                                   + vncChannel + "/&reconnect=1&resize=scale&autoconnect=true&password=" + password.value;
+          }
+        else
+          window.location.href = mumbleURL;
+        document.getElementById ("clear_room").selected = true;
+        return false;
+      }
+
+    </script>
+    <form onsubmit="return enterRoom ();">
+      Room:
+      <select name="room" id="room" onchange="roomChanged ();">
+        <option selected="selected" value="none" id="clear_room">--- please select ---</option>
+        <option value="Conference%20Room">Conference Room</option>
+        <option value="Hörsaal%201">Hörsaal 1</option>
+        <option value="Hörsaal%202">Hörsaal 2</option>
+        <option value="Seminarraum%201">Seminarraum 1</option>
+        <option value="Seminarraum%202">Seminarraum 2</option>
+        <option value="Seminarraum%203">Seminarraum 3</option>
+        <option value="Seminarraum%204">Seminarraum 4</option>
+        <option value="Seminarraum%205">Seminarraum 5</option>
+        <option value="Seminarraum%206">Seminarraum 6</option>
+        <option value="Seminarraum%207">Seminarraum 7</option>
+        <option value="Besprechungsraum%201">Besprechungsraum 1</option>
+        <option value="Besprechungsraum%202">Besprechungsraum 2</option>
+        <option value="Raum%201">Raum 1</option>
+        <option value="Raum%202">Raum 2</option>
+        <option value="Raum%203">Raum 3</option>
+        <option value="Raum%204">Raum 4</option>
+        <option value="Raum%205">Raum 5</option>
+        <option value="Raum%206">Raum 6</option>
+      </select>
+      <br/>
+      <table border="0" cellpadding="0" cellspacing="30">
+        <tr valign="top">
+          <td>
+            <input type="radio" name="vnc_or_mumble" id="vnc_only" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="vnc_only">video only (audio via Mumble)</label><br/>
+            <input type="radio" name="vnc_or_mumble" id="vnc_and_mumble" checked="checked" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="vnc_and_mumble">video and audio (2 tabs)</label><br/>
+            <input type="radio" name="vnc_or_mumble" id="mumble_only" disabled="true" onchange="vncOrMumbleChanged ();"/>
+            <label for="mumble_only">audio only (Mumble web client)</label>
+          </td>
+          <td>
+            <input type="radio" name="resolution" id="full_resolution" checked="checked" disabled="true"/>
+            <label for="full_resolution">full resolution</label><br/>
+            <input type="radio" name="resolution" id="half_resolution" disabled="true"/>
+            <label for="half_resolution">half resolution</label><br/>
+          </td>
+        </tr>
+      </table>
+      <table border="0" cellpadding="0" cellspacing="5">
+        <tr>
+          <td id="password_label" style="color: gray;">Password for VNC:</td>
+          <td><input type="password" size="25" id="password" disabled="true"/></td>
+        </tr>
+        <tr valign="top">
+          <td id="user_name_label" style="color: gray;">Username for Mumble: &nbsp;</td>
+          <td>
+            <input type="text" size="25" id="user_name" disabled="true"/><br/>
+            <font size="-3">(e.&thinsp;g.&nbsp;pgerwinski for “Peter Gerwinski”)</font>
+          </td>
+        </tr>
+      </table>
+      <br/>
+      <button type="submit" id="submit" disabled="true">Enter Room</button>
+    </form>
+    <p>
+      Contact:
+      <a href="mailto:peter.gerwinski@hs-bochum.de">Prof. Dr. Peter Gerwinski</a>
+    </p>
+  </body>
+</html>
diff --git a/www/virtual-rooms/index.html b/www/virtual-rooms/index.html
new file mode 120000
index 0000000000000000000000000000000000000000..69d7d961a6016ff4a9a51f8a4c024dfdc6c2742d
--- /dev/null
+++ b/www/virtual-rooms/index.html
@@ -0,0 +1 @@
+index.de.html
\ No newline at end of file
diff --git a/www/virtual-rooms/logo-hochschule-bochum-cvh-vertikal-v2-128x205.png b/www/virtual-rooms/logo-hochschule-bochum-cvh-vertikal-v2-128x205.png
new file mode 100644
index 0000000000000000000000000000000000000000..79011af023680fe0f231aa7f6233615aa14fbf4b
Binary files /dev/null and b/www/virtual-rooms/logo-hochschule-bochum-cvh-vertikal-v2-128x205.png differ
diff --git a/www/virtual-rooms/stylesheets/default.css b/www/virtual-rooms/stylesheets/default.css
new file mode 100644
index 0000000000000000000000000000000000000000..73d1e40fd7d42cdc3b12d85c606eacb5b920742b
--- /dev/null
+++ b/www/virtual-rooms/stylesheets/default.css
@@ -0,0 +1,38 @@
+/*
+  Web Interface for Virtual Rooms at Campus Velbert/Heiligenhaus,
+  Bochum University of Applied Sciences
+
+  Copyright 2020  Peter Gerwinski <http://www.peter.gerwinski.de>
+
+  This program is free software: you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation, either version 3 of
+  the License, or (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+body
+{
+  font-family: sans-serif;
+  max-width: 50em;
+}
+img.logo
+{
+  margin-left: 2em;
+  margin-bottom: 2em;
+}
+input, select, button
+{
+  font-size: 90%;
+}
+input:disabled+label
+{
+  color: #c0c0c0;
+}