diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index 703b4c18ee552270272ef0c1abac32ec4c857ff4..1d32a0981535c1ee7b611679c21af8154cf78d31 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -223,8 +223,9 @@ struct _glist
     struct _scalehandle *x_handle;
     struct _scalehandle *x_mhandle;
     t_pd *gl_svg;
-    t_symbol *gl_templatename; /* for "canvas" data type */
+    t_symbol *gl_templatesym; /* for "canvas" data type */
     t_word *gl_vec;            /* for "canvas" data type */
+    t_gpointer gl_gp;            /* parent for "canvas" data type */
 };
 
 #define gl_gobj gl_obj.te_g
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 2f620ffba479b8e2c2eeb931d768acc8a1be44f3..30637623704c4573498929952070db8f4f2b5fb0 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -2307,6 +2307,8 @@ t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
     return (rval);
 }
 
+extern int scalar_hascanvasfield(t_scalar *x);
+
     /* right-clicking on a canvas object pops up a menu. */
 static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
 {
@@ -2314,7 +2316,7 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
     if (x->gl_editor->e_onmotion != MA_NONE) return;
     int canprop, canopen, isobject;
     t_gobj *y = NULL;
-    int x1, y1, x2, y2;
+    int x1, y1, x2, y2, scalar_has_canvas = 0;
     if (x->gl_editor->e_selection)
     {
         glist_noselect(x);
@@ -2344,6 +2346,14 @@ static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y_sel)
     canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd)))
                /*&& !canvas_isabstraction( ((t_glist*)y) )*/ );
     canopen = (y && zgetfn(&y->g_pd, gensym("menu-open")));
+    /* we add an extra check for scalars to enable the "Open" button
+       if they happen to have a canvas field inside them. */
+    if (pd_class(&y->g_pd) == scalar_class)
+    {
+        if (scalar_hascanvasfield((t_scalar *)y))
+            scalar_has_canvas = 1;
+        canopen = scalar_has_canvas;
+    }
     if (y || x->gl_editor->e_selection)
     {
         isobject = 1;
diff --git a/pd/src/g_scalar.c b/pd/src/g_scalar.c
index 13767fb4663109a754129ce395d12a3daf07e06e..cbb4355f8ef2b8346980a7f3839960c7f6b466c8 100644
--- a/pd/src/g_scalar.c
+++ b/pd/src/g_scalar.c
@@ -18,10 +18,11 @@ t_class *scalar_class;
 
 void pd_doloadbang(void);
 
-void word_init(t_word *wp, t_template *template, t_gpointer *gp)
+void word_init(t_word *data, t_template *template, t_gpointer *gp)
 {
     int i, nitems = template->t_n;
     t_dataslot *datatypes = template->t_vec;
+    t_word *wp = data;
     for (i = 0; i < nitems; i++, datatypes++, wp++)
     {
         int type = datatypes->ds_type;
@@ -60,11 +61,23 @@ void word_init(t_word *wp, t_template *template, t_gpointer *gp)
             glob_setfilename(0, &s_, &s_);
 
             wp->w_list = canvas_getcurrent();
+            wp->w_list->gl_templatesym = template->t_sym;
+            /* this is bad-- we're storing a reference to a position in
+               a dynamically allocated byte array when realloc can potentially
+               move this data.  Essentially, we're depending on gcc to never
+               move it, which is a bad assumption.  Unfortunately gpointers
+               do the same thing, and I haven't heard back from Miller yet
+               on how he plans to deal with this problem. Hopefully that same
+               solution will be usable here. */
+            wp->w_list->gl_vec = data;
+            /* Here too we're being dangerous-- I'm not unsetting this
+               gpointer yet. */
+            gpointer_copy(gp, &wp->w_list->gl_gp);
 
             while ((x != s__X.s_thing) && s__X.s_thing) 
             {
                 x = s__X.s_thing;
-                vmess(x, gensym("pop"), "i", 1);
+                vmess(x, gensym("pop"), "i", 0);
             }
             /* oops, can't actually do a loadbang here.
                Why?
@@ -242,12 +255,27 @@ int template_cancreate(t_template *template)
     return (template_check_array_fields(0, template) == 1);
 }
 
+int scalar_hascanvasfield(t_scalar *x)
+{
+    t_template *template = template_findbyname(x->sc_template);
+    if (template)
+    {
+        int i, nitems = template->t_n;
+        t_dataslot *datatypes = template->t_vec;
+        for (i = 0; i < nitems; i++, datatypes++)
+        {
+            if (datatypes->ds_type == DT_LIST)
+                return 1;
+        }
+    }
+    return 0;
+}
+
     /* make a new scalar and add to the glist.  We create a "gp" here which
     will be used for array items to point back here.  This gp doesn't do
     reference counting or "validation" updates though; the parent won't go away
     without the contained arrays going away too.  The "gp" is copied out
     by value in the word_init() routine so we can throw our copy away. */
-
 t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym)
 {
     t_scalar *x;
@@ -1197,6 +1225,11 @@ static void scalar_save(t_gobj *z, t_binbuf *b)
     binbuf_free(b2);
 }
 
+static void scalar_menuopen(t_scalar *x)
+{
+    post("tried to open a thing");
+}
+
 static void scalar_properties(t_gobj *z, struct _glist *owner)
 {
     t_scalar *x = (t_scalar *)z;
@@ -1256,6 +1289,8 @@ void g_scalar_setup(void)
         CLASS_GOBJ, 0);
     class_addmethod(scalar_class, (t_method)scalar_mouseover,
         gensym("mouseover"), A_FLOAT, A_NULL);
+    class_addmethod(scalar_class, (t_method)scalar_menuopen,
+        gensym("menu-open"), 0);
     class_setwidget(scalar_class, &scalar_widgetbehavior);
     class_setsavefn(scalar_class, scalar_save);
     class_setpropertiesfn(scalar_class, scalar_properties);
diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c
index ce4cae884f39a031213eb1b8349d05b422618c91..10c1709a5a066ad7e0db5dd5c4e41aa3a48e1eef 100644
--- a/pd/src/g_traversal.c
+++ b/pd/src/g_traversal.c
@@ -13,6 +13,7 @@ getsize - get the size of an array
 setsize - change the size of an array
 append -  add an element to a list
 sublist - get a pointer into a list which is an element of another scalar
+field -   get numeric/symbolic field while within a canvas field
 
 */
 
@@ -1305,6 +1306,112 @@ static void sublist_setup(void)
     class_addpointer(sublist_class, sublist_pointer); 
 }
 
+/* ---------------------- field ----------------------------- */
+
+static t_class *field_class;
+
+typedef struct _field
+{
+    t_object x_obj;
+    t_symbol *x_s;
+    t_template *x_template;
+    t_canvas *x_canvas;
+} t_field;
+
+static void *field_new(t_symbol *s)
+{
+    t_field *x = (t_field *)pd_new(field_class);
+    x->x_s = s;
+    x->x_canvas = canvas_getcurrent();
+    // do some error checking here
+    x->x_template = template_findbyname(x->x_canvas->gl_templatesym);
+    outlet_new(&x->x_obj, &s_list);
+    return (x);
+}
+
+static void field_set(t_field *x, t_symbol *s)
+{
+    x->x_s = s;
+}
+
+static void field_bang(t_field *x)
+{
+    post("sanity is %d", x->x_canvas->sanity);
+    t_word *vec = x->x_canvas->gl_vec;
+    t_template *template = x->x_template;
+    t_symbol *fieldsym = x->x_s;
+    int onset, type;
+    t_symbol *arraytype;
+    if (template_find_field(template, fieldsym, &onset, &type, &arraytype))
+    {
+        if (type == DT_FLOAT)
+            outlet_float(x->x_obj.ob_outlet,
+                *(t_float *)(((char *)vec) + onset));
+        else if (type == DT_SYMBOL)
+            outlet_symbol(x->x_obj.ob_outlet,
+                *(t_symbol **)(((char *)vec)+ onset));
+        else pd_error(x, "field: %s.%s is not a number or symbol",
+                template->t_sym->s_name, fieldsym->s_name);
+    }
+    else pd_error(x, "field: %s.%s: no such field",
+        template->t_sym->s_name, fieldsym->s_name);
+}
+
+static void field_setvalue(t_field *x, t_symbol *s, int argc, t_atom *argv)
+{
+    t_template *template = x->x_template;
+    t_word *vec = x->x_canvas->gl_vec;
+    t_gpointer *gp = &x->x_canvas->gl_gp;
+    t_gstub *gs = gp->gp_stub;
+    int onset, type;
+    t_symbol *arraytype, *fieldsym = x->x_s;
+    if (argc)
+    {
+        if (argv->a_type == A_FLOAT)
+            template_setfloat(template, fieldsym, vec,
+                atom_getfloatarg(0, argc, argv), 1);
+        else if (argv->a_type == A_SYMBOL)
+            template_setsymbol(template, fieldsym, vec,
+                atom_getsymbolarg(0, argc, argv), 1);
+        else pd_error(x, "field: incoming value must be float or symbol");
+    }
+    if (gp->gp_stub->gs_which == GP_GLIST)
+        scalar_configure((t_scalar *)(gp->gp_un.gp_gobj), gs->gs_un.gs_glist);  
+    else
+    {
+        t_array *owner_array = gs->gs_un.gs_array;
+        while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY)
+            owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array;
+        scalar_redraw((t_scalar *)(owner_array->a_gp.gp_un.gp_gobj),
+            owner_array->a_gp.gp_stub->gs_un.gs_glist);  
+    }
+}
+
+static void field_symbol(t_field *x, t_symbol *s)
+{
+    t_atom at[1];
+    SETSYMBOL(at, s);
+    field_setvalue(x, gensym("symbol"), 1, at);
+}
+
+static void field_float(t_field *x, t_floatarg f)
+{
+    t_atom at[1];
+    SETFLOAT(at, f);
+    field_setvalue(x, gensym("float"), 1, at);
+}
+
+static void field_setup(void)
+{
+    field_class = class_new(gensym("field"), (t_newmethod)field_new,
+        0, sizeof(t_field), 0, A_DEFSYM, 0);
+    class_addfloat(field_class, field_float); 
+    class_addsymbol(field_class, field_symbol); 
+    class_addbang(field_class, field_bang); 
+    class_addmethod(field_class, (t_method)field_set, gensym("set"),
+        A_SYMBOL, 0); 
+}
+
 /* ----------------- setup function ------------------- */
 
 void g_traversal_setup(void)
@@ -1317,4 +1424,5 @@ void g_traversal_setup(void)
     setsize_setup();
     append_setup();
     sublist_setup();
+    field_setup();
 }