diff --git a/python/morphology.cpp b/python/morphology.cpp
index 6f99190189d3756e588e03796e5e82f916bbd41b..4c2047d539bfb01e0a1c4073eae73d4634ad6895 100644
--- a/python/morphology.cpp
+++ b/python/morphology.cpp
@@ -84,11 +84,10 @@ void register_morphology(pybind11::module& m) {
             "If loaded from standard SWC file the following tags are used: soma=1, axon=2, dendrite=3, apical dendrite=4, however arbitrary tags can be used.")
         .def("__str__",
             [](const arb::msample& s) {
-                return util::pprintf("<arbor.mpoint: x {}, y {}, z {}, radius {}, tag {}>",
-                        s.loc.x, s.loc.y, s.loc.z, s.loc.radius, s.tag); })
+                return util::pprintf("{}", s);})
         .def("__repr__",
             [](const arb::msample& s) {
-                return util::pprintf("{}>", s);});
+                return util::pprintf("{}", s);});
 
     // arb::mcable
     pybind11::class_<arb::mcable> cable(m, "cable");
@@ -123,8 +122,23 @@ void register_morphology(pybind11::module& m) {
         .def(pybind11::init<>())
         // modifiers
         .def("reserve", &arb::sample_tree::reserve)
-        .def("append", [](arb::sample_tree& t, arb::msample s){return t.append(s);})
-        .def("append", [](arb::sample_tree& t, arb::msize_t p, arb::msample s){return t.append(p, s);})
+        .def("append", [](arb::sample_tree& t, arb::msample s){return t.append(s);},
+                "Append a sample whose parent is the last sample added to the tree.")
+        .def("append", [](arb::sample_tree& t, arb::msize_t p, arb::msample s){return t.append(p, s);},
+                "parent"_a, "sample"_a,
+                "Append a sample.")
+        .def("append",
+                [](arb::sample_tree& t, double x, double y, double z, double radius, int tag) {
+                    return t.append(arb::msample{{x,y,z,radius}, tag});
+                },
+                "x"_a, "y"_a, "z"_a, "radius"_a, "tag"_a,
+                "Append a sample whose parent is the last sample added to the tree.")
+        .def("append",
+                [](arb::sample_tree& t, arb::msize_t p, double x, double y, double z, double radius, int tag) {
+                    return t.append(p, arb::msample{{x,y,z,radius}, tag});
+                },
+                "parent"_a, "x"_a, "y"_a, "z"_a, "radius"_a, "tag"_a,
+                "Append a sample.")
         // properties
         .def_property_readonly("empty", [](const arb::sample_tree& st){return st.empty();},
                 "Indicates whether the sample tree is empty (i.e. whether it has size 0)")
@@ -132,6 +146,8 @@ void register_morphology(pybind11::module& m) {
                 "The number of samples in the sample tree.")
         .def_property_readonly("parents", [](const arb::sample_tree& st){return st.parents();},
                 "A list with the parent index of each sample.")
+        .def_property_readonly("samples", [](const arb::sample_tree& st){return st.samples();},
+                "A list of the samples.")
         .def("__str__", [](const arb::sample_tree& s) {
                 return util::pprintf("<arbor.sample_tree:\n{}>", s);});
 
diff --git a/python/single_cell_model.cpp b/python/single_cell_model.cpp
index 036477652662c9d4f3e6cf38136d87ddbcaa51d1..ae69e3e2df6b7665bfd89ff928d246591c97740c 100644
--- a/python/single_cell_model.cpp
+++ b/python/single_cell_model.cpp
@@ -229,7 +229,9 @@ void register_single_cell(pybind11::module& m) {
         .def_readonly("variable", &trace::variable, "Name of the variable being recorded.")
         .def_readonly("location", &trace::loc, "Location on cell morphology.")
         .def_readonly("time",    &trace::t, "Time stamps of samples [ms].")
-        .def_readonly("value",   &trace::v, "Sample values.");
+        .def_readonly("value",   &trace::v, "Sample values.")
+        .def("__str__", [](const trace& tr) {return util::pprintf("(trace \"{}\" {})", tr.variable, tr.loc);})
+        .def("__repr__", [](const trace& tr) {return util::pprintf("(trace \"{}\" {})", tr.variable, tr.loc);});
 
     pybind11::class_<single_cell_model> model(m, "single_cell_model",
         "Wrapper for simplified description, and execution, of single cell models.");
diff --git a/python/tutorial/example1.py b/python/tutorial/example1.py
new file mode 100644
index 0000000000000000000000000000000000000000..43bfd5c7ce4f2a275b1d26f82195ae0d344c6d0f
--- /dev/null
+++ b/python/tutorial/example1.py
@@ -0,0 +1,56 @@
+import arbor
+
+# Create a sample tree with a single sample of radius 3 μm
+tree = arbor.sample_tree()
+tree.append(arbor.msample(x=0, y=0, z=0, radius=3, tag=2))
+
+labels = arbor.label_dict({'soma': '(tag 2)', 'center': '(location 0 0.5)'})
+
+cell = arbor.cable_cell(tree, labels)
+
+# Set initial membrane potential everywhere on the cell to -40 mV.
+cell.set_properties(Vm=-40)
+# Put hh dynamics on soma, and passive properties on the dendrites.
+cell.paint('soma', 'hh')
+# Attach stimuli with duration of 2 ms and current of 0.8 nA.
+cell.place('center', arbor.iclamp( 10, 2, 0.8))
+# Add a spike detector with threshold of -10 mV.
+cell.place('center', arbor.spike_detector(-10))
+
+# Make single cell model.
+m = arbor.single_cell_model(cell)
+
+# Attach voltage probes, sampling at 10 kHz.
+m.probe('voltage', 'center',  10000)
+
+# Run simulation for 100 ms of simulated activity.
+tfinal=30
+m.run(tfinal)
+
+# Print spike times.
+if len(m.spikes)>0:
+    print('{} spikes:'.format(len(m.spikes)))
+    for s in m.spikes:
+        print('  {:7.4f}'.format(s))
+else:
+    print('no spikes')
+
+# Plot the recorded voltages over time.
+import matplotlib.pyplot as plt
+fig, ax = plt.subplots()
+for t in m.traces:
+    ax.plot(t.time, t.value)
+
+legend_labels = ['{}: {}'.format(s.variable, s.location) for s in m.traces]
+ax.legend(legend_labels)
+ax.set(xlabel='time (ms)', ylabel='voltage (mV)', title='cell builder demo')
+plt.xlim(0,tfinal)
+plt.ylim(-80,50)
+ax.grid()
+
+# Set to True to save the image to file instead of opening a plot window.
+plot_to_file=False
+if plot_to_file:
+    fig.savefig("voltages.png", dpi=300)
+else:
+    plt.show()