diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 41de49aed641786618325d1dd9d9b0820282ac7d..a9f5cbac0ff07ca5d99dcc5ea6a1cdc2e5f528c5 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -3760,6 +3760,7 @@ function gui_drawimage_vis(cid, x, y, obj, data, seqno, parent_tag) {
             len = image_array.length,
             i,
             image_container,
+            xy_container,
             obj_tag = "draw" + obj.slice(1) + "." + data.slice(1);
         if (len < 1) {
             return;
@@ -3768,6 +3769,19 @@ function gui_drawimage_vis(cid, x, y, obj, data, seqno, parent_tag) {
         if (seqno >= len || seqno < 0) {
             seqno %= len;
         }
+        // Since sprites can have lots of images we don't want to
+        // set props on each one of them every time the user changes
+        // an attribute's value. So we use a "g" to receive all the
+        // relevant changes.
+        // Unfortunately "g" doesn't have x/y attys. So it can't propagate
+        // those values down to the child images. Thus we have to add
+        // another "g" as a parent and manually convert x/y changes
+        // the user makes to a transform. (And we can't use the inner g's
+        // transform because the user can set their own transform there.)
+        xy_container = create_item(cid, "g", {
+            id: obj_tag + "xy",
+            transform: "translate(" + x + "," + y + ")"
+        });
         image_container = create_item(cid, "g", {
             id: obj_tag
         });
@@ -3784,7 +3798,8 @@ function gui_drawimage_vis(cid, x, y, obj, data, seqno, parent_tag) {
                  image_array[i].data);
             image_container.appendChild(item);
         }
-        frag.appendChild(image_container);
+        xy_container.appendChild(image_container);
+        frag.appendChild(xy_container);
         // Hack to set correct width and height
         for (i = 0; i < len; i++) {
             img_size_setter(cid, obj_tag+i, pd_cache.get(obj)[i].type,
@@ -3794,6 +3809,14 @@ function gui_drawimage_vis(cid, x, y, obj, data, seqno, parent_tag) {
     });
 }
 
+// Hack
+function gui_drawimage_xy(cid, obj, data, x, y) {
+    var obj_tag = "draw" + obj.slice(1) + "." + data.slice(1);
+    gui(cid).get_elem(obj_tag + "xy", {
+        transform: "translate(" + x + "," + y + ")"
+    });
+}
+
 function gui_drawimage_index(cid, obj, data, index) {
     var obj_tag = "draw" + obj.slice(1) + "." + data.slice(1);
     gui(cid).get_elem(obj_tag, function(image_container) {
diff --git a/pd/src/g_template.c b/pd/src/g_template.c
index 9e4997457c12d1b3371789dac66f0f55a731285c..f0dd2e4d5941b9ee1ee31a7b5e1c5c84556c15c1 100644
--- a/pd/src/g_template.c
+++ b/pd/src/g_template.c
@@ -1210,7 +1210,7 @@ typedef struct _draw
 typedef struct _drawimage
 {
     t_object x_obj;
-    t_fielddesc x_value; /* todo: rename to index */
+    t_fielddesc x_index;
     t_fielddesc x_vis;
     t_symbol *x_img;
     t_float x_w;
@@ -1964,6 +1964,18 @@ void svg_sendupdate(t_svg *x, t_canvas *c, t_symbol *s,
         gui_vmess("gui_drawimage_index", "xxxi",
             glist_getcanvas(c), parent, data, drawimage_getindex(parent, template, data));
     }
+    else if (s == gensym("image_xy"))
+    {
+        /* Hack to deal with x/y for sprite/image. They have an extra
+           container so that we don't have to deal with changing each
+           image in the sequence here. */
+        gui_vmess("gui_drawimage_xy", "xxxff",
+            glist_getcanvas(c), parent, data,
+            x->x_x.a_flag ?
+                fielddesc_getcoord(&x->x_x.a_attr, template, data, 0) : 0,
+            x->x_y.a_flag ?
+                fielddesc_getcoord(&x->x_y.a_attr, template, data, 0) : 0);
+    }
     else if (s == gensym("viewbox"))
     {
         gui_start_vmess("gui_draw_viewbox", "xss",
@@ -3991,10 +4003,10 @@ static void svg_togui(t_svg *x, t_template *template, t_word *data)
         gui_f(fielddesc_getcoord(&x->x_rx.a_attr, template, data, 0));
     }
     if (x->x_ry.a_flag)
-        {
+    {
             gui_s(x->x_type == gensym("ellipse") ? "ry" : "y2");
             gui_f(fielddesc_getcoord(&x->x_ry.a_attr, template, data, 0));
-        }
+    }
     if (x->x_x.a_flag)
     {
         gui_s(x->x_type == gensym("rect") || x->x_type == gensym("svg") ? "x" :
@@ -8013,6 +8025,7 @@ static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv)
     x->x_flags = flags;
     x->x_attr = (t_pd *)sa;
     fielddesc_setfloat_const(&x->x_vis, 1);
+    fielddesc_setfloat_const(&x->x_index, 1);
     x->x_canvas = canvas_getcurrent();
     t_symbol *dir = canvas_getdir(x->x_canvas);
     if (argc && argv->a_type == A_SYMBOL)
@@ -8026,11 +8039,12 @@ static void *drawimage_new(t_symbol *classsym, int argc, t_atom *argv)
     /* outlet for event notifications */
     outlet_new(&x->x_obj, &s_anything);
 
-    /* [drawimage] allocates memory for an image or image sequence
-       while the object is creating. The corresponding scalar gets
-       drawn as a canvas image item using the "parent" tk image as
-       the source. ".x%lx" is the name for the parent tk image and
-       ".x%lx.i" is the tag given to a scalar's canvas image item.
+    /* [drawimage] allocates memory for an image or image sequence in
+       the GUI. When we "vis" the scalar we fetch the data from the
+       loaded images in the sequence. Then we can change the image shown
+       by just sending an index to the GUI where it displays that particular
+       image in the sequence without having to touch I/O or even parse
+       image data and render a new image.
     */
     gui_vmess("gui_drawimage_new", "xssi",
         x,
@@ -8044,13 +8058,12 @@ void drawimage_size(t_drawimage *x, t_float w, t_float h)
 {
     x->x_w = w;
     x->x_h = h;
-    //post("w is %g and h is %g", w, h);
 }
 
 static int drawimage_getindex(void *z, t_template *template, t_word *data)
 {
     t_drawimage *x = (t_drawimage *)z;
-    int index = (int)fielddesc_getcoord(&x->x_value, template, data, 1);
+    int index = (int)fielddesc_getcoord(&x->x_index, template, data, 1);
     return (index);
 }
 
@@ -8063,7 +8076,7 @@ static void drawimage_index(t_drawimage *x, t_symbol *s, int argc,
         if (!(x->x_flags & DRAW_SPRITE))
             post("drawimage warning: sequence variable is only "
                  "used with drawsprite");
-        fielddesc_setfloatarg(&x->x_value, argc, argv);
+        fielddesc_setfloatarg(&x->x_index, argc, argv);
         svg_update((t_svg *)x->x_attr, gensym("index"));
     }
 }
@@ -8089,19 +8102,30 @@ void drawimage_symbol(t_drawimage *x, t_symbol *s)
    attributes to the parent <g> for convenience, but <g> has no x/y atty.
 
    We could just forward everything to the child <image> element, but that
-   could get clunky when dealing with large image sequences. So for now we
-   just disallow setting the x/y with the knowledge that the user can get
-   the same functionality using a transform. */
-static void drawimage_x(t_drawimage *x, t_symbol *s, int argc,
-    t_atom *argv)
-{
-    pd_error(x, "draw: x attribute for image type not supported");
-}
+   could get clunky when dealing with large image sequences.
 
-static void drawimage_y(t_drawimage *x, t_symbol *s, int argc,
+   So for now we hack around this by adding a <g> above our parent <g>, then
+   converting the x/y in the GUI to a transform for that <g>. This is ugly
+   and gets in the way of the interface built here, but it should work.
+*/
+static void drawimage_xy(t_drawimage *x, t_symbol *s, int argc,
     t_atom *argv)
 {
-    pd_error(x, "draw: y attribute for image type not supported");
+    t_svg *sa = (t_svg *)x->x_attr;
+    t_svg_attr *attr = svg_getattr(sa, s);
+    if (!attr)
+    {
+        pd_error(x, "draw: can't find attribute %s", s->s_name);
+        return;
+    }
+    if (argc < 1)
+        attr->a_flag = 0;
+    else if (argv[0].a_type == A_FLOAT || argv[0].a_type == A_SYMBOL)
+    {
+        fielddesc_setfloatarg(&attr->a_attr, argc, argv);
+        attr->a_flag = 1;
+        svg_update(sa, gensym("image_xy"));
+    }
 }
 
 static void drawimage_anything(t_drawimage *x, t_symbol *s, int argc,
@@ -8262,7 +8286,7 @@ static void drawimage_vis(t_gobj *z, t_glist *glist, t_glist *parentglist,
             yloc,
             x,
             data,
-            (int)fielddesc_getfloat(&x->x_value, template, data, 0),
+            (int)fielddesc_getfloat(&x->x_index, template, data, 0),
             parent_tagbuf);
 
         gui_start_vmess("gui_draw_configure_all", "xs",
@@ -8296,7 +8320,7 @@ static void drawimage_motion(void *z, t_floatarg dx, t_floatarg dy)
 {
     t_drawimage *x = (t_drawimage *)z;
     t_svg *sa = (t_svg *)x->x_attr;
-    t_fielddesc *f = &x->x_value;
+    t_fielddesc *f = &x->x_index;
     t_atom at;
     if (!gpointer_check(&drawimage_motion_gpointer, 0))
     {
@@ -8408,7 +8432,7 @@ static int drawimage_click(t_gobj *z, t_glist *glist,
         data, template, basex, basey,
         &x1, &y1, &x2, &y2);
     if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2
-        && x->x_value.fd_var &&
+        && x->x_index.fd_var &&
             fielddesc_getfloat(&x->x_vis, template, data, 0))
     {
         if (doit)
@@ -8420,7 +8444,7 @@ static int drawimage_click(t_gobj *z, t_glist *glist,
             drawimage_motion_array = ap;
             drawimage_motion_firstkey = 1;
             drawimage_motion_ycumulative =
-                fielddesc_getfloat(&x->x_value, template, data, 0);
+                fielddesc_getfloat(&x->x_index, template, data, 0);
             drawimage_motion_sprite = ((x->x_flags & DRAW_SPRITE) != 0);
             if (drawimage_motion_scalar)
                 gpointer_setglist(&drawimage_motion_gpointer, 
@@ -8457,15 +8481,12 @@ static void drawimage_free(t_drawimage *x)
 
 static void drawimage_setup(void)
 {
-    /* we need drawimage_class in order to get
-       a different set of widget behavior than
-       draw_class. But we also want to use the
-       [draw shape] syntax for consistency. So
-       for class_new we set the constructor to
-       zero and call drawimage_new from inside
-       draw_new. This way the user has to type
-       "draw image" or "draw sprite" to create
-       the objects. */
+    /* we need drawimage_class in order to get a different set of
+       widget behavior than draw_class. But we also want to use the
+       [draw shape] syntax for consistency. So for class_new we set
+       the constructor to zero and call drawimage_new from inside
+       draw_new. This way the user has to type "draw image" or "draw sprite"
+       to create the objects. */
     drawimage_class = class_new(gensym("drawimage"),
         0, (t_method)drawimage_free,
         sizeof(t_drawimage), 0, A_GIMME, 0);
@@ -8476,9 +8497,9 @@ static void drawimage_setup(void)
         gensym("size"), A_FLOAT, A_FLOAT, 0);
     class_addmethod(drawimage_class, (t_method)drawimage_index,
         gensym("index"), A_GIMME, 0);
-    class_addmethod(drawimage_class, (t_method)drawimage_x,
+    class_addmethod(drawimage_class, (t_method)drawimage_xy,
         gensym("x"), A_GIMME, 0);
-    class_addmethod(drawimage_class, (t_method)drawimage_y,
+    class_addmethod(drawimage_class, (t_method)drawimage_xy,
         gensym("y"), A_GIMME, 0);
     class_addanything(drawimage_class, drawimage_anything);
     class_setparentwidget(drawimage_class, &drawimage_widgetbehavior);
@@ -8589,6 +8610,12 @@ void svg_parentwidgettogui(t_gobj *z, t_scalar *sc, t_glist *owner,
         gui_vmess("gui_drawimage_index", "xxxi",
             glist_getcanvas(owner), x, data,
             drawimage_getindex(x, template, data));
+        gui_vmess("gui_drawimage_xy", "xxxff",
+            glist_getcanvas(owner), x, data,
+            fielddesc_getcoord(&((t_svg *)x->x_attr)->x_x.a_attr,
+                template, data, 0),
+            fielddesc_getcoord(&((t_svg *)x->x_attr)->x_y.a_attr,
+                template, data, 0));
     }
     else if (pd_class(&z->g_pd) == curve_class)
     {