diff --git a/static/example/example.html b/static/example/example.html
index acdfca8..e300fc2 100644
--- a/static/example/example.html
+++ b/static/example/example.html
@@ -7,12 +7,14 @@
   </head>
 
   <body>
-    <p id="status">Disconnected</p>
+    <div><button id="start">Start</button></p>
+
+    <p id="status"></p>
     <p id="error"></p>
 
     <div><button id="show" disabled>Show/hide yourself</button></div>
 
-    <video id="video"></video>
+    <div id="videos"></div>
 
     <div id="chat"></div>
 
diff --git a/static/example/example.js b/static/example/example.js
index d442bb3..ddbbe29 100644
--- a/static/example/example.js
+++ b/static/example/example.js
@@ -1,4 +1,4 @@
-/* Galene client example.  Send-only for now. */
+/* Galene client example. */
 
 /**
  * The main function.
@@ -59,11 +59,7 @@ async function serverConnect(status, token) {
     };
     conn.onchat = onChat;
     conn.onusermessage = onUserMessage;
-    conn.ondownstream = function(s) {
-        // This should not happen, since we didn't ask to receive streams.
-        console.warn('Received unexpected stream from server');
-        s.abort();
-    }
+    conn.ondownstream = onDownStream;
     conn.onclose = function() {
         displayStatus('Disconnected');
     }
@@ -183,6 +179,8 @@ async function onJoined(kind, group, perms, status, data, error, message) {
     case 'change':
         displayStatus(`Connected as ${this.username} in group ${this.group}.`);
         enableShow(this, true);
+        // request videos from the server
+        this.request({'': ['audio', 'video']});
         break;
     default:
         displayError(`Unexpected state ${kind}.`);
@@ -191,22 +189,50 @@ async function onJoined(kind, group, perms, status, data, error, message) {
     }
 }
 
+/**
+ * Create a video element.  We encode the stream's id in the element's id
+ * in order to avoid having a global hash table that maps ids to video
+ * elements.
+ *
+ * @parm {string} id
+ * @returns {HTMLVideoElement}
+ */
+function makeVideoElement(id) {
+    let v = document.createElement('video');
+    v.id = 'video-' + id;
+    let container = document.getElementById('videos');
+    container.appendChild(v);
+    return v;
+}
+
+/**
+ * Find the video element that shows a given id.
+ *
+ * @parm {string} id
+ * @returns {HTMLVideoElement}
+ */
+function getVideoElement(id) {
+    let v = document.getElementById('video-' + id);
+    return /** @type{HTMLVideoElement} */(v);
+}
+
 /**
  * Enable the camera and broadcast yourself to the group.
  *
  * @parm {ServerConnection} conn
  */
 async function showCamera(conn) {
-    let v = /** @type HTMLVideoElement */(document.getElementById('video'));
     let ms = await navigator.mediaDevices.getUserMedia({audio: true, video: true});
 
     /* Send the new stream to the server */
     let s = conn.newUpStream();
     s.label = 'camera';
     s.setStream(ms);
+    let v = makeVideoElement(s.localId);
     s.onclose = function(replace) {
         s.stream.getTracks().forEach(t => t.stop());
         v.srcObject = null;
+        v.parentNode.removeChild(v);
     }
 
     function addTrack(t) {
@@ -245,6 +271,29 @@ async function hide(conn, s) {
     s.close();
 }
 
+/**
+ * Called when the server pushes a stream.
+ *
+ * @this {ServerConnection}
+ * @parm {Stream} c
+ */
+function onDownStream(s) {
+    s.onclose = function(replace) {
+        let v = getVideoElement(s.localId);
+        v.srcObject = null;
+        v.parentNode.removeChild(v);
+    }
+    s.ondowntrack = function(track, transceiver, stream) {
+        let v = getVideoElement(s.localId);
+        if(v.srcObject !== stream)
+            v.srcObject = stream;
+    }
+
+    let v = makeVideoElement(s.localId);
+    v.srcObject = s.stream;
+    v.play();
+}
+
 /**
  * Display an error message.
  *
@@ -254,4 +303,12 @@ function displayError(message) {
     document.getElementById('error').textContent = message;
 }
 
-start("/group/public/").catch(e => displayError(e));
+document.getElementById('start').onclick = async function(e) {
+    let button = /** @type{HTMLButtonElement} */(this);
+    button.hidden = true;
+    try {
+        await start("/group/public/");
+    } catch(e) {
+        displayError(e);
+    };
+}
