diff --git a/externals/pd-lua b/externals/pd-lua
index b767c65b6607a1b2b80798a5ff4fb3e3c1d752a5..8eaaaa3c39af8968248fab5659d4a4d2da4f1ed7 160000
--- a/externals/pd-lua
+++ b/externals/pd-lua
@@ -1 +1 @@
-Subproject commit b767c65b6607a1b2b80798a5ff4fb3e3c1d752a5
+Subproject commit 8eaaaa3c39af8968248fab5659d4a4d2da4f1ed7
diff --git a/pd/nw/index.js b/pd/nw/index.js
index 8c777871fede74fd90718c7b19d7aaba3885f72c..1a173c91e36f6b477a5851eaec6b225d9b868ee2 100644
--- a/pd/nw/index.js
+++ b/pd/nw/index.js
@@ -584,6 +584,7 @@ function nw_create_pd_window_menus(gui, w) {
         minit(m.edit.reselect, { enabled: false });
     }
     if (osx) {
+        minit(m.edit.encapsulate, { enabled: false });
         minit(m.edit.tidyup, { enabled: false });
         minit(m.edit.font, { enabled: false });
         minit(m.edit.cordinspector, { enabled: false });
diff --git a/pd/nw/locales/en/translation.json b/pd/nw/locales/en/translation.json
index e7a8510191e040baa874336b085caaf65711450b..20b33e60c02c34a670afa83f95fe96a16ea0c30c 100644
--- a/pd/nw/locales/en/translation.json
+++ b/pd/nw/locales/en/translation.json
@@ -144,6 +144,8 @@
     "reselect_tt": "Restore the previous selection",
     "find": "Find",
     "find_tt": "Find text in the console output",
+    "encapsulate": "Encapsulate",
+    "encapsulate_tt": "Encapsulate automatically the current selection into a subpatch",
     "tidyup": "Tidy Up",
     "tidyup_tt": "Line up the selected objects in straight rows and columns",
     "clear_console": "Clear Console",
diff --git a/pd/nw/pd_canvas.js b/pd/nw/pd_canvas.js
index e7bc1ae692329ec6c02e0550b80cdee0ed6baf10..c3374b9e1c97de61883565b30888002578d49ba2 100644
--- a/pd/nw/pd_canvas.js
+++ b/pd/nw/pd_canvas.js
@@ -1626,6 +1626,10 @@ function nw_create_patch_window_menus(gui, w, name) {
             pdgui.pdsend(name, "reselect");
         }
     });
+    minit(m.edit.encapsulate, {
+        enabled: true,
+        click: function() { pdgui.pdsend(name, "encapsulate"); }
+    });
     minit(m.edit.tidyup, {
         enabled: true,
         click: function() { pdgui.pdsend(name, "tidy"); }
diff --git a/pd/nw/pd_menus.js b/pd/nw/pd_menus.js
index cfbc48f25c561eb3a760906db1ce9a31eb4e7700..79e031410c527e0e21d64c1247574ad8dc01f15a 100644
--- a/pd/nw/pd_menus.js
+++ b/pd/nw/pd_menus.js
@@ -278,6 +278,12 @@ function create_menu(gui, type) {
     }));
         edit_menu.append(new gui.MenuItem({ type: "separator" }));
     if (canvas_menu) {
+        edit_menu.append(m.edit.encapsulate = new gui.MenuItem({
+            label: l("menu.encapsulate"),
+            key: shortcuts.menu.encapsulate.key,
+            modifiers: shortcuts.menu.encapsulate.modifiers,
+            tooltip: l("menu.encapsulate_tt")
+        }));
         edit_menu.append(m.edit.tidyup = new gui.MenuItem({
             label: l("menu.tidyup"),
             key: shortcuts.menu.tidyup.key,
diff --git a/pd/nw/pd_shortcuts.js b/pd/nw/pd_shortcuts.js
index 4e140856cd55862a785b271757532fbdfa95d13a..a0f2bfd6055a56953769a165fc6a32d11bc6c0a4 100644
--- a/pd/nw/pd_shortcuts.js
+++ b/pd/nw/pd_shortcuts.js
@@ -24,6 +24,7 @@ exports.menu = {
 
   "reselect": { key: String.fromCharCode(10), modifiers: cmd_or_ctrl },
   "clear_console": { key: "l", modifiers: cmd_or_ctrl + "+shift" },
+  "encapsulate": { key: "e", modifiers: cmd_or_ctrl + "+shift" },
   "tidyup": { key: "y", modifiers: cmd_or_ctrl },
   "cordinspector":   { key: "r", modifiers: cmd_or_ctrl + "+shift" },
   "find":   { key: "f", modifiers: cmd_or_ctrl },
diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c
index 15dfc84f01240e40f99a3d87cfda7f54eba0e0eb..e2e2c65ff6db5927d52c089940668473ee20579a 100644
--- a/pd/src/g_editor.c
+++ b/pd/src/g_editor.c
@@ -6280,6 +6280,270 @@ static void canvas_cut(t_canvas *x)
     }
 }
 
+/* ------------------------- encapsulate ------------------------ */
+
+typedef struct _xletholder
+{
+    t_gobj *xlh_addr;
+    int xlh_seln;
+    int xlh_xletn;
+    int xlh_type;
+    int xlh_x;
+    int xlh_y;
+    t_binbuf *xlh_conn;
+    struct _xletholder *xlh_next;
+} t_xletholder;
+
+#define X_PLAOFF 40
+#define Y_PLAOFF 80
+#define XLETSGAP 10
+
+static void canvas_dofancycopy(t_canvas *x, t_binbuf **object, t_binbuf **connections)
+{
+    t_gobj *y;
+    t_linetraverser t;
+    t_outconnect *oc;
+    t_binbuf *b = binbuf_new();
+    int maxx, maxy, minx, miny, numobj = 0;
+    maxx = maxy = 0;
+    minx = miny = INT_MAX;
+
+    binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"),
+        0, 0, 450, 300, gensym("subpatch"), 0);
+
+    /* compute global rect */
+    for (y = x->gl_list; y; y = y->g_next)
+    {
+        if (glist_isselected(x, y))
+        {
+            int tminx, tmaxx, tminy, tmaxy;
+            gobj_getrect(y, x, &tminx, &tminy, &tmaxx, &tmaxy);
+            if(tminx < minx) minx = tminx;
+            if(tmaxx > maxx) maxx = tmaxx;
+            if(tminy < miny) miny = tminy;
+            if(tmaxy > maxy) maxy = tmaxy;
+        }
+    }
+
+    /* save selected objects into binbbuf */
+    for (y = x->gl_list; y; y = y->g_next)
+    {
+        if (glist_isselected(x, y))
+        {
+            gobj_displace(y, x, X_PLAOFF-minx, Y_PLAOFF-miny);
+            gobj_save(y, b);
+            gobj_displace(y, x, minx-X_PLAOFF, miny-Y_PLAOFF);
+            numobj++;
+        }
+    }
+
+    /* traverse all the lines */
+    t_xletholder *inlets = 0, *outlets = 0, *lastoutlet = 0;
+    linetraverser_start(&t, x);
+    while (oc = linetraverser_next(&t))
+    {
+        int s1 = glist_isselected(x, &t.tr_ob->ob_g);
+        int s2 = glist_isselected(x, &t.tr_ob2->ob_g);
+        /* if inner connection */
+        if (s1 && s2)
+        {
+            binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
+                glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno,
+                glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno);
+        }
+        /* if outer outlet connection */
+        else if(s1 && !s2)
+        {
+            /* if we continue in the same outlet, we add the object and inlet number to the list and skip */
+            if (lastoutlet && lastoutlet->xlh_addr == t.tr_outlet)
+            {
+                binbuf_addv(lastoutlet->xlh_conn, "ii", glist_selectionindex(x, &t.tr_ob2->ob_g, 0), t.tr_inno);
+                continue;
+            }
+
+            /* create and fill outlet handling structure */
+            t_xletholder *newoutlet = (t_xletholder *)getbytes(sizeof(t_xletholder));
+            newoutlet->xlh_addr = t.tr_outlet;
+            newoutlet->xlh_seln = glist_selectionindex(x, &t.tr_ob->ob_g, 1);
+            newoutlet->xlh_xletn = t.tr_outno;
+            newoutlet->xlh_type = obj_issignaloutlet(t.tr_ob, t.tr_outno);
+            newoutlet->xlh_x = t.tr_lx1;
+            newoutlet->xlh_y = t.tr_ly1;
+            newoutlet->xlh_conn = binbuf_new();
+            binbuf_addv(newoutlet->xlh_conn, "ii", glist_selectionindex(x, &t.tr_ob2->ob_g, 0), t.tr_inno);
+            /* insert structure regarding its coords */
+            t_xletholder *prev = 0, *curr = outlets;
+            while(curr && (t.tr_lx1 > curr->xlh_x || (t.tr_lx1 == curr->xlh_x && t.tr_ly1 > curr->xlh_y)))
+            {
+                prev = curr;
+                curr = curr->xlh_next;
+            }
+            if(prev) prev->xlh_next = newoutlet;
+            else outlets = newoutlet;
+            newoutlet->xlh_next = curr;
+
+            lastoutlet = newoutlet;
+        }
+        /* if outer inlet connection */
+        else if(!s1 && s2)
+        {
+            /* check if inlet is already visited */
+            int alr = 0;
+            t_xletholder *it;
+            for(it = inlets; it && !alr; )
+            {
+                alr = ((t.tr_inno ? t.tr_inlet : t.tr_ob2) == it->xlh_addr);
+                if(!alr) it = it->xlh_next;
+            }
+
+            int type = obj_issignaloutlet(t.tr_ob, t.tr_outno);
+            /* if so, we add the object and outlet number to the list and skip */
+            if(alr)
+            {
+                binbuf_addv(it->xlh_conn, "ii", glist_selectionindex(x, &t.tr_ob->ob_g, 0), t.tr_outno);
+                if(it->xlh_type >= 0 && it->xlh_type != type) it->xlh_type = -1;
+                continue;
+            }
+
+            /* create and fill inlet handling structure */
+            t_xletholder *newinlet = (t_xletholder *)getbytes(sizeof(t_xletholder));
+            newinlet->xlh_addr = (t.tr_inno ? t.tr_inlet : t.tr_ob2);
+            newinlet->xlh_seln = glist_selectionindex(x, &t.tr_ob2->ob_g, 1);
+            newinlet->xlh_xletn = t.tr_inno;
+            newinlet->xlh_type = type;
+            newinlet->xlh_x = t.tr_lx2;
+            newinlet->xlh_y = t.tr_ly2;
+            newinlet->xlh_conn = binbuf_new();
+            binbuf_addv(newinlet->xlh_conn, "ii", glist_selectionindex(x, &t.tr_ob->ob_g, 0), t.tr_outno);
+            /* insert structure regarding its coords */
+            t_xletholder *prev = 0, *curr = inlets;
+            while(curr && (t.tr_lx2 > curr->xlh_x || (t.tr_lx2 == curr->xlh_x && t.tr_ly2 > curr->xlh_y)))
+            {
+                prev = curr;
+                curr = curr->xlh_next;
+            }
+            if(prev) prev->xlh_next = newinlet;
+            else inlets = newinlet;
+            newinlet->xlh_next = curr;
+        }
+    }
+
+    /* store outer connections in a binbuf */
+    t_binbuf *conns = binbuf_new();
+    int subnum = glist_selectionindex(x, 0, 0), nin = 0, nout = 0, xplac = 0;
+    while(inlets)
+    {
+        if(inlets->xlh_type >= 0)
+        {
+            binbuf_addv(b, "ssiis;", gensym("#X"), gensym("obj"),
+                X_PLAOFF/3 + xplac, Y_PLAOFF/3, (inlets->xlh_type ? gensym("inlet~") : gensym("inlet")));
+
+            xplac += (inlets->xlh_type ? 46 : 40) + XLETSGAP; //hardcoded
+        }
+        else
+        {
+            binbuf_addv(b, "ssiiss;", gensym("#X"), gensym("obj"),
+                X_PLAOFF/3 + xplac, Y_PLAOFF/3, gensym("inlet~"), gensym("fwd"));
+
+            xplac += 64 + XLETSGAP; //hardcoded
+
+            binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
+            numobj+nin, 1,
+            inlets->xlh_seln, inlets->xlh_xletn);
+        }
+
+        binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
+            numobj+nin, 0,
+            inlets->xlh_seln, inlets->xlh_xletn);
+
+
+        int nvec = binbuf_getnatom(inlets->xlh_conn);
+        t_atom *vec = binbuf_getvec(inlets->xlh_conn);
+        for(int i = 0; i < nvec/2; i++)
+        {
+            binbuf_addv(conns, "ssffii;", gensym("#X"), gensym("connect"),
+                atom_getfloat(vec+(2*i)), atom_getfloat(vec+(2*i+1)), subnum, nin);
+        }
+        binbuf_free(inlets->xlh_conn);
+        t_xletholder *tmp = inlets->xlh_next;
+        freebytes(inlets, sizeof(t_xletholder));
+        inlets = tmp;
+        nin++;
+    }
+    xplac = 0;
+    while(outlets)
+    {
+        binbuf_addv(b, "ssiis;", gensym("#X"), gensym("obj"),
+            X_PLAOFF/3 + xplac, (maxy-miny) + Y_PLAOFF*5/3, (outlets->xlh_type ? gensym("outlet~") : gensym("outlet")));
+
+        xplac += (outlets->xlh_type ? 52 : 46) + XLETSGAP; //hardcoded
+
+        binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"),
+            outlets->xlh_seln, outlets->xlh_xletn,
+            numobj+nin+nout, 0);
+
+        int nvec = binbuf_getnatom(outlets->xlh_conn);
+        t_atom *vec = binbuf_getvec(outlets->xlh_conn);
+        for(int i = 0; i < nvec/2; i++)
+        {
+            binbuf_addv(conns, "ssiiff;", gensym("#X"), gensym("connect"),
+                subnum, nout, atom_getfloat(vec+(2*i)), atom_getfloat(vec+(2*i+1)));
+        }
+        binbuf_free(outlets->xlh_conn);
+        t_xletholder *tmp = outlets->xlh_next;
+        freebytes(outlets, sizeof(t_xletholder));
+        outlets = tmp;
+        nout++;
+    }
+
+    int m = (nin > nout ? nin : nout);
+    int width = (IOWIDTH * m) * 2 - IOWIDTH;
+
+    binbuf_addv(b, "ssiiss;", gensym("#X"), gensym("restore"),
+        (minx+maxx)/2 - width/2, (miny+maxy)/2, gensym("pd"), gensym("[name]"));
+
+    *object = b;
+    *connections = conns;
+}
+
+static void canvas_encapsulate(t_canvas *x)
+{
+    /* check preconditions */
+    // num selected objects > 0
+    if (!x->gl_editor || !x->gl_editor->e_selection)
+        return;
+
+    canvas_undo_add(x, UNDO_SEQUENCE_START, "encapsulate", 0);
+    /* cut selected objects using special copy method, based on canvas_cut */
+    t_binbuf *object, *connections;
+    int centx, centy;
+    canvas_undo_add(x, UNDO_CUT, "cut", canvas_undo_set_cut(x, UCUT_CUT));
+    canvas_dofancycopy(x, &object, &connections);
+    canvas_doclear(x);
+    glob_preset_node_list_check_loc_and_update();
+    scrollbar_update(x);
+
+    /* subcanvas creation */
+    canvas_dopaste(x, object);
+    /* trace connections between unselectd objects and patch object */
+    pd_bind(&x->gl_pd, gensym("#X"));
+    binbuf_eval(connections, 0, 0, 0);
+    pd_unbind(&x->gl_pd, gensym("#X"));
+
+    canvas_undo_add(x, UNDO_CREATE, "create", canvas_undo_set_create(x));
+
+    binbuf_free(object);
+    binbuf_free(connections);
+
+    /* select subpatch object */
+    t_gobj *sub = glist_nth(x, glist_getindex(x, 0)-1);
+    gobj_activate(sub, x, (0b1 << 31) | (0x0003 << 16) | 0x0009);
+
+    canvas_undo_add(x, UNDO_SEQUENCE_END, "encapsulate", 0);
+}
+
+/* ------------------------------------------------- */
+
 static int paste_onset;
 static t_canvas *paste_canvas;
 
@@ -7773,6 +8037,8 @@ void g_editor_setup(void)
         gensym("redo"), A_NULL);
     class_addmethod(canvas_class, (t_method)canvas_tidy,
         gensym("tidy"), A_NULL);
+    class_addmethod(canvas_class, (t_method)canvas_encapsulate,
+        gensym("encapsulate"), A_NULL);
     //class_addmethod(canvas_class, (t_method)canvas_texteditor,
     //    gensym("texteditor"), A_NULL);
     class_addmethod(canvas_class, (t_method)canvas_editmode,
diff --git a/scripts/regression_tests.pd b/scripts/regression_tests.pd
index af06729fe90d15f2662ec9080e59b6473be54d4d..4b8a8c20c7713b30c986cb19b34e0e4d0c16751c 100644
--- a/scripts/regression_tests.pd
+++ b/scripts/regression_tests.pd
@@ -1,7 +1,7 @@
-#N canvas 245 72 750 572 12;
+#N canvas -9 -9 771 392 12;
 #X obj 465 281 r \$0-result;
-#X obj 212 239 bng 15 250 50 0 empty empty Run_all 17 7 0 10 -262144
--1 -1;
+#X obj 212 239 bng 15 250 50 0 empty empty Run_all 17 7 0 10 #fcfcfc
+#000000 #000000;
 #X obj 56 25 r init;
 #X obj 345 191 route dollarzero;
 #X obj 198 281 rtest msg_dollarzero;
@@ -65,6 +65,7 @@ is handy for some binbuf tests.;
 #X obj 198 2361 rtest inlet~_no_fwd;
 #X obj 198 2416 rtest inlet~_fwd_large_message;
 #X obj 198 2471 rtest pow~_negative_numbers;
+#X obj 198 2526 rtest encapsulate;
 #X connect 0 0 27 0;
 #X connect 1 0 4 0;
 #X connect 2 0 42 0;
@@ -115,3 +116,4 @@ is handy for some binbuf tests.;
 #X connect 56 0 57 0;
 #X connect 57 0 58 0;
 #X connect 58 0 59 0;
+#X connect 59 0 60 0;
diff --git a/scripts/regression_tests/encapsulate.pd b/scripts/regression_tests/encapsulate.pd
new file mode 100644
index 0000000000000000000000000000000000000000..7e3fe921e3879876ec60b6e76d3422ce258d77bf
--- /dev/null
+++ b/scripts/regression_tests/encapsulate.pd
@@ -0,0 +1,126 @@
+#N canvas 472 0 948 963 10;
+#X obj 794 292 s pd-encapsulate_test.pd;
+#X obj 55 22 inlet;
+#X obj 65 674 outlet;
+#X obj 174 61 t b b;
+#X obj 101 318 float;
+#X obj 101 273 until;
+#X obj 193 318 bang;
+#X msg 101 238 bang;
+#X obj 86 90 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 -1
+-1;
+#X msg 794 96 editmode 1;
+#X msg 794 180 encapsulate;
+#X obj 651 42 t b b b b b b b b b;
+#X msg 169 271 0;
+#X msg 794 208 obj_addtobuf pd test;
+#X msg 794 236 obj_buftotext;
+#X msg 794 264 noselect;
+#X obj 102 404 list cat;
+#X obj 102 361 moses 5;
+#X obj 103 489 unpack f f f f f;
+#X obj 103 447 spigot;
+#X obj 410 236 float 1;
+#X obj 298 567 ==;
+#X obj 331 567 ==;
+#X obj 367 567 ==;
+#X obj 401 567 ==;
+#X obj 437 567 ==;
+#X obj 491 251 float 0;
+#X msg 512 209 clear;
+#X obj 371 168 t b b b;
+#X obj 474 168 t b b b;
+#X obj 404 611 &&;
+#X obj 452 611 + 0;
+#X obj 386 645 float;
+#X obj 606 312 float;
+#X obj 606 267 until;
+#X obj 698 312 bang;
+#X msg 606 232 bang;
+#X msg 674 265 0;
+#X obj 607 398 list cat;
+#X obj 607 355 moses 5;
+#X obj 608 483 unpack f f f f f;
+#X obj 608 441 spigot;
+#X obj 653 312 + 1;
+#X msg 794 124 mouse 100 280 1 0;
+#X msg 794 152 mouseup 250 380 0;
+#X obj 148 318 + 1;
+#X obj 65 621 list append encapsulate feature error;
+#X connect 1 0 3 0;
+#X connect 3 1 11 0;
+#X connect 4 0 17 0;
+#X connect 4 0 45 0;
+#X connect 5 0 4 0;
+#X connect 6 0 5 1;
+#X connect 7 0 5 0;
+#X connect 8 0 3 0;
+#X connect 9 0 0 0;
+#X connect 10 0 0 0;
+#X connect 11 0 28 0;
+#X connect 11 1 29 0;
+#X connect 11 2 15 0;
+#X connect 11 3 14 0;
+#X connect 11 4 13 0;
+#X connect 11 5 10 0;
+#X connect 11 6 44 0;
+#X connect 11 7 43 0;
+#X connect 11 8 9 0;
+#X connect 12 0 4 1;
+#X connect 13 0 0 0;
+#X connect 14 0 0 0;
+#X connect 15 0 0 0;
+#X connect 16 0 19 0;
+#X connect 17 0 16 0;
+#X connect 17 1 6 0;
+#X connect 18 0 21 0;
+#X connect 18 1 22 0;
+#X connect 18 2 23 0;
+#X connect 18 3 24 0;
+#X connect 18 4 25 0;
+#X connect 19 0 18 0;
+#X connect 20 0 30 1;
+#X connect 20 0 41 1;
+#X connect 20 0 19 1;
+#X connect 21 0 30 0;
+#X connect 22 0 30 0;
+#X connect 23 0 30 0;
+#X connect 24 0 30 0;
+#X connect 25 0 30 0;
+#X connect 26 0 41 1;
+#X connect 26 0 19 1;
+#X connect 27 0 38 1;
+#X connect 27 0 16 1;
+#X connect 28 0 32 0;
+#X connect 28 1 38 0;
+#X connect 28 1 16 0;
+#X connect 28 2 20 0;
+#X connect 29 0 36 0;
+#X connect 29 0 7 0;
+#X connect 29 1 26 0;
+#X connect 29 2 27 0;
+#X connect 29 2 12 0;
+#X connect 30 0 31 0;
+#X connect 30 0 32 0;
+#X connect 31 0 30 1;
+#X connect 32 0 46 0;
+#X connect 33 0 39 0;
+#X connect 33 0 42 0;
+#X connect 34 0 33 0;
+#X connect 35 0 34 1;
+#X connect 36 0 34 0;
+#X connect 37 0 33 1;
+#X connect 38 0 41 0;
+#X connect 39 0 38 0;
+#X connect 39 1 35 0;
+#X connect 40 0 21 1;
+#X connect 40 1 22 1;
+#X connect 40 2 23 1;
+#X connect 40 3 24 1;
+#X connect 40 4 25 1;
+#X connect 41 0 40 0;
+#X connect 42 0 33 1;
+#X connect 43 0 0 0;
+#X connect 44 0 0 0;
+#X connect 45 0 4 1;
+#X connect 46 0 2 0;