diff --git a/nengo_gui/components/__init__.py b/nengo_gui/components/__init__.py
index a46052b6..67167e7d 100644
--- a/nengo_gui/components/__init__.py
+++ b/nengo_gui/components/__init__.py
@@ -8,6 +8,7 @@
from .pointer import Pointer
from .netgraph import NetGraph
from .ace_editor import AceEditor
+from .htmlview import HTMLView
# Old versions of the .cfg files used Templates which had slightly different
# names than the Components currently use. This code allows us to
diff --git a/nengo_gui/components/htmlview.py b/nengo_gui/components/htmlview.py
new file mode 100644
index 00000000..6172678a
--- /dev/null
+++ b/nengo_gui/components/htmlview.py
@@ -0,0 +1,45 @@
+import struct
+import collections
+
+import nengo
+import numpy as np
+
+from nengo_gui.components.component import Component
+
+
+class HTMLView(Component):
+ def __init__(self, obj):
+ super(HTMLView, self).__init__()
+ self.obj = obj
+ self.obj_output = obj.output
+ self.data = collections.deque()
+
+ def attach(self, page, config, uid):
+ super(HTMLView, self).attach(page, config, uid)
+ self.label = page.get_label(self.obj)
+
+ def add_nengo_objects(self, page):
+ with page.model:
+ self.obj.output = self.gather_data
+
+ def remove_nengo_objects(self, page):
+ self.obj.output = self.obj_output
+
+ def gather_data(self, t, *x):
+ value = self.obj_output(t, *x)
+ data = '%g %s' % (t, self.obj_output._nengo_html_)
+ self.data.append(data)
+ return value
+
+ def update_client(self, client):
+ while len(self.data) > 0:
+ item = self.data.popleft()
+ client.write(item)
+
+ def javascript(self):
+ info = dict(uid=id(self), label=self.label)
+ json = self.javascript_config(info)
+ return 'new Nengo.HTMLView(main, sim, %s);' % json
+
+ def code_python_args(self, uids):
+ return [uids[self.obj]]
diff --git a/nengo_gui/components/netgraph.py b/nengo_gui/components/netgraph.py
index 7b6e99ec..e5df5283 100644
--- a/nengo_gui/components/netgraph.py
+++ b/nengo_gui/components/netgraph.py
@@ -433,6 +433,9 @@ def create_object(self, client, obj, type, parent):
info['passthrough'] = True
if type == 'ens' or type == 'node':
info['dimensions'] = int(obj.size_out)
+ if type == 'node':
+ if callable(obj.output) and hasattr(obj.output, '_nengo_html_'):
+ info['html'] = True
info['sp_targets'] = (
nengo_gui.components.pointer.Pointer.applicable_targets(obj))
diff --git a/nengo_gui/examples/basics/html.py b/nengo_gui/examples/basics/html.py
new file mode 100644
index 00000000..29054fc3
--- /dev/null
+++ b/nengo_gui/examples/basics/html.py
@@ -0,0 +1,84 @@
+# Creating Custom Displays
+
+# This demonstrates using the special _nengo_html_ variable to create
+# custom visualizations. In general, this lets you take values from the model
+# and use them to make HTML that displays something useful for your situation.
+#
+# The basic technique is that if you define a function for a Node, in addition
+# to returning a value, you can also set the _nengo_html_ parameter on that
+# function. You can set it to any HTML you want, and that HTML will be
+# displayed in a custom HTML graph (available by right-clicking on the Node).
+
+# The first example is a timer function that says "Ready..." for the
+# first second of the simulation, then says "Set..." for the next second,
+# and then says "Go!". This shows how you can change the HTML based on the
+# time of the simulation.
+#
+# The second example shows that the custom HTML can also be based on the values
+# represented by neurons. Here, we make a Node that reads in a value that is
+# treated as an amount. If the value is above 0.5, the HTML says "large",
+# if the value is below -0.5, it says "small", and otherwise it says "medium".
+#
+# Finally, the third example uses SVG and trigonometry to create a simple
+# visualization of a two-joint arm. The two values represented by the
+# Ensemble are the two joint angles (the shoulder and the elbow). From these
+# values, the position of the elbow and hand are computed, and lines are
+# drawn to show the arm.
+
+import nengo
+import numpy as np
+
+model = nengo.Network()
+with model:
+
+ # Example 1: a timer
+ def timer_function(t):
+ if t < 1.0:
+ timer_function._nengo_html_ = '