diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index e7bc1ae692329ec6c02e0550b80cdee0ed6baf10..d5d133b897fe29db113afafb439f90cbc9d57cc6 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -1338,6 +1338,8 @@ function register_window_id(cid, attr_array) {
         nw.Window.get().title = kludge_title;
     }
     pdgui.free_title_queue(cid);
+
+    if(attr_array.muldirty) pdgui.gui_canvas_multipledirty(cid, 1);
 }
 
 function create_popup_menu(name) {
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index b9ab05f3e8b1f6d99047b0e4898b2dbd76c1a798..6c5bd3063a1e78c1b0b96cece3c447580e7a5a62 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -1607,7 +1607,7 @@ function create_window(cid, type, width, height, xpos, ypos, attr_array) {
 }
 
 // create a new canvas
-function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
+function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir, dirty_flag, muldirty, hide_scroll, hide_menu, has_toplevel_scalars, cargs) {
     // hack for buggy tcl popups... should go away for node-webkit
     //reset_ctrl_on_popup_window
     
@@ -1661,6 +1661,7 @@ function gui_canvas_new(cid, width, height, geometry, zoom, editmode, name, dir,
             name: name,
             dir: dir,
             dirty: dirty_flag,
+            muldirty: muldirty,
             args: cargs,
             zoom: zoom,
             editmode: editmode,
@@ -2675,6 +2676,8 @@ function gui_canvas_multipledirty(cid, state) {
     else warning.style.setProperty("display", "none");
 }
 
+exports.gui_canvas_multipledirty = gui_canvas_multipledirty;
+
 function gui_canvas_emphasize(cid) {
     gui(cid).get_elem("patchsvg", function(e) {
         // raise the window
diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c
index 1d00e47ef37861e89ddd072549999479df7b4ee4..933584cad529599fb03b255928bed49b5c535e80 100644
--- a/pd/src/g_canvas.c
+++ b/pd/src/g_canvas.c
@@ -483,6 +483,7 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
     else x->gl_isab = 0;
 
     x->gl_subdirties = 0;
+    x->gl_dirties = 0;
 
     if (yloc < GLIST_DEFCANVASYLOC)
         yloc = GLIST_DEFCANVASYLOC;
@@ -532,6 +533,15 @@ t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv)
     canvas_field_vec = NULL;
     canvas_field_gp = NULL;
 
+    if(x->gl_env)
+    {
+        if(!x->gl_isab)
+            x->gl_dirties = canvas_dirty_broadcast_all(x->gl_name,
+                                canvas_getdir(x), 0);
+        else
+            x->gl_dirties = canvas_dirty_broadcast_ab_all(x->gl_absource, 0);
+    }
+
     return(x);
 }
 
@@ -786,7 +796,169 @@ void canvas_reflecttitle(t_canvas *x)
         namebuf, canvas_getdir(x)->s_name, x->gl_dirty);
 }
 
-void gobj_dirty(t_glist *g, t_gobj *x, int on);
+/* --------------------- */
+
+void clone_iterate(t_pd *z, t_canvas_iterator it, void* data);
+int clone_match(t_pd *z, t_symbol *name, t_symbol *dir);
+
+typedef struct _dirty_broadcast_data
+{
+    t_symbol *name;
+    t_symbol *dir;
+    int mess;
+    int *res;
+} t_dirty_broadcast_data;
+
+static void canvas_dirty_deliver_packed(t_canvas *x, t_dirty_broadcast_data *data)
+{
+    *data->res += (x->gl_dirty > 0);
+    x->gl_dirties += data->mess;
+    if(x->gl_havewindow)
+        canvas_multipledirty(x, (x->gl_dirties > 1));
+}
+
+static int canvas_dirty_broadcast_packed(t_canvas *x, t_dirty_broadcast_data *data);
+
+static int canvas_dirty_broadcast(t_canvas *x, t_symbol *name, t_symbol *dir, int mess)
+{
+    int res = 0;
+    t_gobj *g;
+    for (g = x->gl_list; g; g = g->g_next)
+    {
+        if(pd_class(&g->g_pd) == canvas_class)
+        {
+            if(canvas_isabstraction((t_canvas *)g) && !((t_canvas *)g)->gl_isab
+                && ((t_canvas *)g)->gl_name == name
+                && canvas_getdir((t_canvas *)g) == dir)
+            {
+                res += (((t_canvas *)g)->gl_dirty > 0);
+                ((t_canvas *)g)->gl_dirties += mess;
+                if(((t_canvas *)g)->gl_havewindow)
+                    canvas_multipledirty((t_canvas *)g, (((t_canvas *)g)->gl_dirties > 1));
+            }
+            else
+                res += canvas_dirty_broadcast((t_canvas *)g, name, dir, mess);
+        }
+        else if(pd_class(&g->g_pd) == clone_class)
+        {
+            int cres = 0;
+            t_dirty_broadcast_data data;
+            data.name = name; data.dir = dir; data.mess = mess; data.res = &cres;
+            if(clone_match(&g->g_pd, name, dir))
+            {
+                clone_iterate(&g->g_pd, canvas_dirty_deliver_packed, &data);
+            }
+            else
+            {
+                clone_iterate(&g->g_pd, canvas_dirty_broadcast_packed, &data);
+            }
+            res += cres;
+        }
+    }
+    return (res);
+}
+
+static int canvas_dirty_broadcast_packed(t_canvas *x, t_dirty_broadcast_data *data)
+{
+    *data->res = canvas_dirty_broadcast(x, data->name, data->dir, data->mess);
+}
+
+int canvas_dirty_broadcast_all(t_symbol *name, t_symbol *dir, int mess)
+{
+    int res = 0;
+    t_canvas *x;
+    for (x = pd_this->pd_canvaslist; x; x = x->gl_next)
+        res += canvas_dirty_broadcast(x, name, dir, mess);
+    return (res);
+}
+
+/* same but for ab */
+
+typedef struct _dirty_broadcast_ab_data
+{
+    t_ab_definition *abdef;
+    int mess;
+    int *res;
+} t_dirty_broadcast_ab_data;
+
+static void canvas_dirty_deliver_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data)
+{
+    *data->res += (x->gl_dirty > 0);
+    x->gl_dirties += data->mess;
+    if(x->gl_havewindow)
+        canvas_multipledirty(x, (x->gl_dirties > 1));
+}
+
+static int canvas_dirty_broadcast_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data);
+
+static int canvas_dirty_broadcast_ab(t_canvas *x, t_ab_definition *abdef, int mess)
+{
+    int res = 0;
+    t_gobj *g;
+    for (g = x->gl_list; g; g = g->g_next)
+    {
+        if(pd_class(&g->g_pd) == canvas_class)
+        {
+            if(canvas_isabstraction((t_canvas *)g) && ((t_canvas *)g)->gl_isab
+                && ((t_canvas *)g)->gl_absource == abdef)
+            {
+                res += (((t_canvas *)g)->gl_dirty > 0);
+                ((t_canvas *)g)->gl_dirties += mess;
+                if(((t_canvas *)g)->gl_havewindow)
+                    canvas_multipledirty((t_canvas *)g, (((t_canvas *)g)->gl_dirties > 1));
+            }
+            else
+                res += canvas_dirty_broadcast_ab((t_canvas *)g, abdef, mess);
+        }
+        else if(pd_class(&g->g_pd) == clone_class)
+        {
+            int cres = 0;
+            t_dirty_broadcast_ab_data data;
+            data.abdef = abdef; data.mess = mess; data.res = &cres;
+            if(clone_matchab(&g->g_pd, abdef))
+            {
+                clone_iterate(&g->g_pd, canvas_dirty_deliver_ab_packed, &data);
+            }
+            else if(clone_isab(&g->g_pd))
+            {
+                clone_iterate(&g->g_pd, canvas_dirty_broadcast_ab_packed, &data);
+            }
+            res += cres;
+        }
+    }
+    return (res);
+}
+
+static int canvas_dirty_broadcast_ab_packed(t_canvas *x, t_dirty_broadcast_ab_data *data)
+{
+    *data->res = canvas_dirty_broadcast_ab(x, data->abdef, data->mess);
+}
+
+int canvas_dirty_broadcast_ab_all(t_ab_definition *abdef, int mess)
+{
+    int res = 0;
+    t_canvas *x;
+    for (x = pd_this->pd_canvaslist; x; x = x->gl_next)
+        res += canvas_dirty_broadcast_ab(x, abdef, mess);
+    return (res);
+}
+
+void canvas_dirtyclimb(t_canvas *x, int n)
+{
+    if (x->gl_owner)
+    {
+        gobj_dirty(&x->gl_gobj, x->gl_owner,
+            (n ? 1 : (x->gl_subdirties ? 2 : 0)));
+        x = x->gl_owner;
+        while(x->gl_owner)
+        {
+            x->gl_subdirties += ((unsigned)n ? 1 : -1);
+            if(!x->gl_dirty)
+                gobj_dirty(&x->gl_gobj, x->gl_owner, (x->gl_subdirties ? 2 : 0));
+            x = x->gl_owner;
+        }
+    }
+}
 
     /* mark a glist dirty or clean */
 void canvas_dirty(t_canvas *x, t_floatarg n)
@@ -799,18 +971,18 @@ void canvas_dirty(t_canvas *x, t_floatarg n)
         x2->gl_dirty = n;
         if (x2->gl_havewindow)
             canvas_reflecttitle(x2);
-        if (x2->gl_owner)
+
+        canvas_dirtyclimb(x2, (unsigned)n);
+
+        if(canvas_isabstraction(x2)
+            && (x2->gl_owner || x2->gl_isclone))
         {
-            gobj_dirty(x2->gl_owner, &x2->gl_gobj,
-                (x2->gl_dirty ? 1 : (x2->gl_subdirties ? 2 : 0)));
-            x2 = x2->gl_owner;
-            while(x2->gl_owner)
-            {
-                x2->gl_subdirties += (n ? 1 : -1);
-                if(!x2->gl_dirty)
-                    gobj_dirty(x2->gl_owner, &x2->gl_gobj, (x2->gl_subdirties ? 2 : 0));
-                x2 = x2->gl_owner;
-            }
+            if(!x2->gl_isab)
+                canvas_dirty_broadcast_all(x2->gl_name, canvas_getdir(x2),
+                    (x2->gl_dirty ? 1 : -1));
+            else
+                canvas_dirty_broadcast_ab_all(x2->gl_absource,
+                    (x2->gl_dirty ? 1 : -1));
         }
     }
 }
@@ -1041,6 +1213,15 @@ static void canvas_deregister_ab(t_canvas *x, t_ab_definition *a);
 void canvas_free(t_canvas *x)
 {
     //fprintf(stderr,"canvas_free %lx\n", (t_int)x);
+
+    if(x->gl_dirty)
+    {
+        if(!x->gl_isab)
+            canvas_dirty_broadcast_all(x->gl_name, canvas_getdir(x), -1);
+        else
+            canvas_dirty_broadcast_ab_all(x->gl_absource, -1);
+    }
+
     t_gobj *y;
     int dspstate = canvas_suspend_dsp();
 
@@ -1079,6 +1260,13 @@ void canvas_free(t_canvas *x)
     if (x->gl_svg)                   /* for groups, free the data */
         canvas_group_free(x->gl_svg);
 
+    /* freeing an ab instance */
+    if(x->gl_isab)
+    {
+        x->gl_absource->ad_numinstances--;
+        canvas_deregister_ab(x->gl_owner, x->gl_absource);
+    }
+
     /* free stored ab definitions */
     t_ab_definition *d = x->gl_abdefs, *daux;
     while(d)
@@ -1090,13 +1278,6 @@ void canvas_free(t_canvas *x)
         freebytes(d, sizeof(t_ab_definition));
         d = daux;
     }
-
-    /* freeing an ab instance */
-    if(x->gl_isab)
-    {
-        x->gl_absource->ad_numinstances--;
-        canvas_deregister_ab(x->gl_owner, x->gl_absource);
-    }
 }
 
 /* ----------------- lines ---------- */
diff --git a/pd/src/g_canvas.h b/pd/src/g_canvas.h
index b4be1fa4b370344edad666011a8ae3537dbbd386..aab227d02ffec0c7c19f0444ce4f81d7c513e1b6 100644
--- a/pd/src/g_canvas.h
+++ b/pd/src/g_canvas.h
@@ -252,6 +252,7 @@ struct _glist
     t_gpointer gl_gp;            /* parent for "canvas" data type */
 
     unsigned int gl_subdirties;     /* number of descending dirty abstractions */
+    int gl_dirties;    /* number of diry instances of this type */
 
     unsigned int gl_isab:1;         /* is an ab instance */
     t_ab_definition *gl_absource;   /* ab definition pointer,
@@ -463,6 +464,7 @@ EXTERN void gobj_save(t_gobj *x, t_binbuf *b);
 EXTERN void gobj_properties(t_gobj *x, struct _glist *glist);
 EXTERN void gobj_save(t_gobj *x, t_binbuf *b);
 EXTERN int gobj_shouldvis(t_gobj *x, struct _glist *glist);
+EXTERN void gobj_dirty(t_gobj *x, t_glist *g, int state);
 
 /* -------------------- functions on glists --------------------- */
 EXTERN t_glist *glist_new( void);
@@ -600,6 +602,8 @@ EXTERN t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos,
 EXTERN int canvas_setdeleting(t_canvas *x, int flag);
 EXTERN int canvas_hasarray(t_canvas *x);
 
+EXTERN void canvas_multipledirty(t_canvas *x, int on);
+
 #define LB_LOAD 0       /* "loadbang" actions - 0 for original meaning */
 #define LB_INIT 1       /* loaded but not yet connected to parent patch */
 #define LB_CLOSE 2      /* about to close */
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 424802d7e1fa29c3921e68bf62a89133d68d3754..519f577353e2fee83830cbd0b62855e0919c45b7 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -1288,6 +1288,7 @@ void canvas_undo_paste(t_canvas *x, void *z, int action)
     }
 }
 
+void canvas_dirtyclimb(t_canvas *x, int n);
 void clone_iterate(t_pd *z, t_canvas_iterator it, void* data);
 int clone_match(t_pd *z, t_symbol *name, t_symbol *dir);
 int clone_isab(t_pd *z);
@@ -1326,13 +1327,9 @@ static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir,
                 ((t_canvas *)g)->gl_name == name &&
                     canvas_getdir((t_canvas *)g) == dir);
 
-        if(remakeit && ((t_canvas *)g)->gl_dirty)
         /* set dirty to 0 to remove the dirty markings*/
-        {
-            glist_amreloadingabstractions = 0;
-            canvas_dirty((t_canvas *)g, 0);
-            glist_amreloadingabstractions = 1;
-        }
+        if(remakeit && ((t_canvas *)g)->gl_dirty)
+            canvas_dirtyclimb((t_canvas *)g, 0);
 
             /* also remake it if it's a "clone" with that name */
         if (pd_class(&g->g_pd) == clone_class &&
@@ -1455,13 +1452,9 @@ static void glist_doreload_ab(t_canvas *x, t_ab_definition *a, t_gobj *e)
         remakeit = (g != e && pd_class(&g->g_pd) == canvas_class && canvas_isabstraction((t_canvas *)g)
             && ((t_canvas *)g)->gl_isab && ((t_canvas *)g)->gl_absource == a);
 
-        if(remakeit && ((t_canvas *)g)->gl_dirty)
         /* set dirty to 0 to remove the dirty markings*/
-        {
-            glist_amreloadingabstractions = 0;
-            canvas_dirty((t_canvas *)g, 0);
-            glist_amreloadingabstractions = 1;
-        }
+        if(remakeit && ((t_canvas *)g)->gl_dirty)
+            canvas_dirtyclimb((t_canvas *)g, 0);
 
         remakeit = remakeit || (pd_class(&g->g_pd) == clone_class && clone_matchab(&g->g_pd, a));
 
@@ -2610,7 +2603,7 @@ void canvas_vis(t_canvas *x, t_floatarg f)
                We may need to expand this to include scalars, as well. */
             canvas_create_editor(x);
             canvas_args_to_string(argsbuf, x);
-            gui_vmess("gui_canvas_new", "xiisiissiiiis",
+            gui_vmess("gui_canvas_new", "xiisiissiiiiis",
                 x,
                 (int)(x->gl_screenx2 - x->gl_screenx1),
                 (int)(x->gl_screeny2 - x->gl_screeny1),
@@ -2620,6 +2613,7 @@ void canvas_vis(t_canvas *x, t_floatarg f)
                 x->gl_name->s_name,
                 canvas_getdir(x)->s_name,
                 x->gl_dirty,
+                (x->gl_dirty && x->gl_dirties > 1),
                 x->gl_noscroll,
                 x->gl_nomenu,
                 canvas_hasarray(x),
@@ -5968,15 +5962,15 @@ static void gobj_emphasize(t_glist *g, t_gobj *x)
     gui_vmess("gui_gobj_emphasize", "xs", g, rtext_gettag(y));
 }
 
-void gobj_dirty(t_glist *g, t_gobj *x, int on)
+void gobj_dirty(t_gobj *x, t_glist *g, int state)
 {
     t_rtext *y = glist_findrtext(g, (t_text *)x);
-    gui_vmess("gui_gobj_dirty", "xsi", g, rtext_gettag(y), on);
+    gui_vmess("gui_gobj_dirty", "xsi", g, rtext_gettag(y), state);
 }
 
-void canvas_multipledirty(t_canvas *c, int on)
+void canvas_multipledirty(t_canvas *x, int on)
 {
-    gui_vmess("gui_canvas_multipledirty", "xi", c, on);
+    gui_vmess("gui_canvas_multipledirty", "xi", x, (on > 0));
 }
 
 static int glist_dofinderror(t_glist *gl, void *error_object)
diff --git a/pd/src/g_text.c b/pd/src/g_text.c
index 41e793bc34b1c5932f4bcce18016603aeb79189e..501dd0b0cb296d1537fba474d689dd562f6fbe31 100644
--- a/pd/src/g_text.c
+++ b/pd/src/g_text.c
@@ -2125,9 +2125,9 @@ static void text_vis(t_gobj *z, t_glist *glist, int vis)
                 if(pd_class(&x->te_pd) == canvas_class)
                 {
                     if (((t_canvas *)x)->gl_dirty)
-                        gobj_dirty(glist, x, 1);
+                        gobj_dirty(x, glist, 1);
                     else if (((t_canvas *)x)->gl_subdirties)
-                        gobj_dirty(glist, x, 2);
+                        gobj_dirty(x, glist, 2);
                 }
             }
         }