diff --git a/pd/nw/index.js b/pd/nw/index.js
index 37ae7ba42b8a1b0bdb8d5867f393cd21cb4f5a89..c05d91d606704e73f91678b6a6b58e7ed31ac6f3 100644
--- a/pd/nw/index.js
+++ b/pd/nw/index.js
@@ -25,7 +25,7 @@ function have_args() {
 }
 
 function set_vars(win) {
-    var port_no, gui_dir, font_engine_sanity;
+    var port_no, gui_dir, font_engine_sanity, pd_engine_id;
     // If the GUI was started by Pd, our port number is going to be
     // the first argument. If the GUI is supposed to start Pd, we won't
     // have any arguments and need to set it here.
@@ -33,6 +33,8 @@ function set_vars(win) {
         port_no = gui.App.argv[0]; // fed to us by the Pd process
         // looks like this is the same as pwd below
         gui_dir = gui.App.argv[3];
+        // address unique to the pd_engine
+        pd_engine_id = gui.App.argv[4];
     } else {
         // If we're starting Pd, this is the first port number to try. (We'll
         // increment it if that port happens to be taken.
@@ -44,6 +46,7 @@ function set_vars(win) {
         gui_dir = process.platform === "darwin" ? "bin" : pwd;
     }
     pdgui.set_port(port_no);
+    pdgui.set_pd_engine_id(pd_engine_id);
     pdgui.set_pwd(pwd);
     pdgui.set_gui_dir(gui_dir);
     pdgui.set_pd_window(win);
@@ -256,6 +259,14 @@ function add_events() {
             pdgui.pdsend("pd dsp", dsp_state);
         }
     );
+    // Opening another file
+    nw.App.on("open", function(argv_string) {
+        var port, pd_engine_id;
+        port = argv_string.split(" ").slice(-5, -4);
+        pd_engine_id = argv_string.split(" ").slice(-1);
+        pdgui.connect_as_client_to_secondary_instance("localhost", port,
+            pd_engine_id);
+    });
     // Browser Window Close
     gui.Window.get().on("close", function () {
         pdgui.menu_quit();
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 84bc392d3f74f7a2342935b61c01c3e618afdc61..ef3c25aa76efe3c2e4235fb1de8a116b19a05d62 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -4,6 +4,7 @@ var pwd;
 var gui_dir;
 var lib_dir;
 var last_clipboard_data;
+var pd_engine_id;
 
 exports.set_pwd = function(pwd_string) {
     pwd = pwd_string;
@@ -21,6 +22,10 @@ function defunkify_windows_path(s) {
     return ret;
 }
 
+exports.set_pd_engine_id = function (id) {
+    pd_engine_id = id;
+}
+
 exports.defunkify_windows_path = defunkify_windows_path;
 
 exports.set_gui_dir = function(dir_string) {
@@ -217,8 +222,6 @@ var pd_myversion,    // Pd version string
 
     popup_coords = [0,0];
 
-    var startup_files = []; // Array of files to be opened at startup (from the command line)
-
 // Keycode vs Charcode: A Primer
 // -----------------------------
 // * keycode is a unique number assigned to a physical key on the keyboard
@@ -786,19 +789,6 @@ function open_file(file) {
     }
 }
 
-// This doesn't work yet... need to figure out how to send command line args
-// (files) to be opened by the unique instance
-function gui_open_files_via_unique(filenames)
-{
-    var i;
-    length = filenames.length;
-    if (length != 0) {
-        for (i = 0; i < length; i++) {
-            open_file(filenames[i]);
-        }
-    }
-}
-
 function open_html(target) {
     nw_open_html(target);
 }
@@ -845,34 +835,29 @@ function external_doc_open(url) {
 
 exports.external_doc_open = external_doc_open;
 
-function gui_build_filelist(file) {
-    startup_files.push(file);
+function gui_set_cwd(dummy, cwd) {
+    post("cwd from secondary instance is " + cwd);
+    if (cwd !== ".") {
+        pwd = cwd;
+    }
 }
 
 // This doesn't work at the moment.  Not sure how to feed the command line
 // filelist to a single instance of node-webkit.
-function gui_check_unique (unique) {
-    // global appname
-    return;
-    var final_filenames = new Array,
-        startup_dir = pwd,
-        filelist_length,
+function gui_open_via_unique (secondary_pd_engine_id, unique, file_array) {
+    var startup_dir = pwd,
         i,
-        file,
-        dir;
-    if (unique == 0) {
-        filelist_length = startup_files.length;
-        for (i = 0; i < filelist_length; i++) {
-            file = startup_files[i];
-            dir;
-            if (!pathIsAbsolute(file)) {
-                file = fs.join(pwd, file);
+        file;
+    if (unique == 0 && secondary_pd_engine_id !== pd_engine_id) {
+        for (i = 0; i < file_array.length; i++) {
+            file = file_array[i];
+            if (!path.isAbsolute(file)) {
+                file = path.join(pwd, file);
             }
-            final_filenames.push(file);
+            open_file(file);
         }
-        gui_open_files_via_unique(final_filenames);
+        quit_secondary_pd_instance(secondary_pd_engine_id);
     }
-    // old tcl follows... see pd.tk for the original code
 }
 
 function gui_startup(version, fontname_from_pd, fontweight_from_pd,
@@ -1209,6 +1194,85 @@ exports.set_port = function (port_no) {
     PORT = port_no;
 }
 
+var secondary_pd_engines = {};
+
+// This is an alarmingly complicated and brittle approach to opening
+// files from a secondary instance of Pd in a currently running instance.
+// It works something like this:
+// 1. User is running an instance of Purr Data.
+// 2. User runs another instance of Purr Data from the command line, specifying
+//    files to be opened as command line args. Or, they click on a file which
+//    in the desktop or file manager which triggers the same behavior.
+// 2. A new Pd process starts-- let's call it a "secondary pd engine".
+// 3. The secondary pd engine tries to run a new GUI.
+// 4. The secondary GUI forwards an "open" message to the currently running GUI.
+// 5. The secondary GUI exits (before spawning any windows).
+// 6. The original GUI receives the "open" message, finds the port number
+//    for the secondary Pd engine, and opens a socket to it.
+// 7. The original GUI receives a message to set the working directory to
+//    whatever the secondary Pd engine thinks it should be.
+// 8. The original GUI sends a message to the secondary Pd instance, telling
+//    it to send a list of files to be opened.
+// 9. The original GUI receives a message from the secondary Pd instance
+//    with the list of files.
+// 10.For each file to be opened, the original GUI sends a message to the
+//    _original_ Pd engine to open the file.
+// 11.Once these messages have been sent, the original GUI sends a message
+//    to the secondary Pd engine to quit.
+// 12.The original Pd engine opens the files, and the secondary Pd instance
+//    quits.
+function connect_as_client_to_secondary_instance(host, port, pd_engine_id) {
+    var client = new net.Socket(),
+        command_buffer = {
+            next_command: ""
+    };
+    client.setNoDelay(true);
+    client.connect(+port, host, function() {
+        console.log("CONNECTED TO: " + host + ":" + port);
+        secondary_pd_engines[pd_engine_id] = {
+            socket: client
+        }
+        client.write("pd forward_files_from_secondary_instance;");
+    });
+    client.on("data", function(data) {
+        // Terrible thing:
+        // We're parsing the data as it comes in-- possibly
+        // from multiple ancillary instances of the Pd engine.
+        // So to retain some semblance of sanity, we only let the
+        // parser evaluate commands that we list in the array below--
+        // anything else will be discarded.  This is of course bad
+        // because it means simple changes to the code, e.g., changing
+        // the name of the function "gui_set_cwd" would cause a bug
+        // if you forget to come here and also change that name in the
+        // array below.
+        // Another terrible thing-- gui_set_cwd sets a single, global
+        // var for the working directory. So if the user does something
+        // weird like write a script to open files from random directories,
+        // there would be a race and it might not work.
+        // Yet another terrible thing-- now we're setting the current
+        // working directory both in the GUI, and from the secondary instances
+        // with "gui_set_cwd" below.
+        perfect_parser(data, command_buffer, [
+            "gui_set_cwd",
+            "gui_open_via_unique"
+        ]);
+    });
+    client.on("close", function () {
+        // I guess somebody could script opening patches in an
+        // installation, so let's go ahead and delete the key here
+        // (The alternative is just setting it to undefined)
+        delete secondary_pd_engines[pd_engine_id];
+    });
+}
+
+function quit_secondary_pd_instance (pd_engine_id) {
+    secondary_pd_engines[pd_engine_id].socket.write("pd quit;");
+}
+
+// This is called when the running GUI receives an "open" event.
+exports.connect_as_client_to_secondary_instance =
+    connect_as_client_to_secondary_instance;
+
 function connect_as_client() {
     var client = new net.Socket();
     client.setNoDelay(true);
@@ -1261,17 +1325,12 @@ exports.connect_as_server = connect_as_server;
 // data parameter is what the server sent to this socket
 
 // We're not receiving FUDI (i.e., Pd) messages. Right now we're just using
-// an alarm bell '\a' to signal the end of a message. This is easier than
-// checking for unescaped semicolons, since it only requires a check for a
-// single byte. Of course this makes it more brittle, so it can be changed
-// later if needed.
+// the unit separator (ASCII 31) to signal the end of a message. This is
+// easier than checking for unescaped semicolons, since it only requires a
+// check for a single byte. Of course this makes it more brittle, so it can
+// be changed later if needed.
 
-function init_socket_events () {
-    var next_command = ""; // A not-quite-FUDI command: selector arg1,arg2,etc.
-                           // These are formatted on the C side to be easy
-                           // to parse here in javascript
-
-    var perfect_parser = function(data) {
+function perfect_parser(data, cbuf, sel_array) {
         var i, len, selector, args;
         len = data.length;
         for (i = 0; i < len; i++) {
@@ -1280,24 +1339,33 @@ function init_socket_events () {
                 // decode next_command
                 try {
                     // This should work for all utf-8 content
-                    next_command = decodeURIComponent(next_command);
+                    cbuf.next_command =
+                        decodeURIComponent(cbuf.next_command);
                 }
                 catch(err) {
                     // This should work for ISO-8859-1
-                    next_command = unescape(next_command);
+                    cbuf.next_command = unescape(cbuf.next_command);
                 }
                 // Turn newlines into backslash + "n" so
                 // eval will do the right thing with them
-                next_command = next_command.replace(/\n/g, "\\n");
-                next_command = next_command.replace(/\r/g, "\\r");
-                selector = next_command.slice(0, next_command.indexOf(" "));
-                args = next_command.slice(selector.length + 1);
-                next_command = "";
+                cbuf.next_command = cbuf.next_command.replace(/\n/g, "\\n");
+                cbuf.next_command = cbuf.next_command.replace(/\r/g, "\\r");
+                selector = cbuf.next_command.slice(0, cbuf.next_command.indexOf(" "));
+                args = cbuf.next_command.slice(selector.length + 1);
+                cbuf.next_command = "";
                 // Now evaluate it
                 //post("Evaling: " + selector + "(" + args + ");");
-                eval(selector + "(" + args + ");");
+                // For communicating with a secondary instance, we filter
+                // incoming messages. A better approach would be to make
+                // sure that the Pd engine only sends the gui_set_cwd message
+                // before "gui_startup".  Then we could just check the
+                // Pd engine id in "gui_startup" and branch there, instead of
+                // fudging with the parser here.
+                if (!sel_array || sel_array.indexOf(selector) !== -1) {
+                    eval(selector + "(" + args + ");");
+                }
             } else {
-                next_command += "%" +
+                cbuf.next_command += "%" +
                     ("0" // leading zero (for rare case of single digit)
                      + data[i].toString(16)) // to hex
                        .slice(-2); // remove extra leading zero
@@ -1305,31 +1373,15 @@ function init_socket_events () {
         }
     };
 
-    var fast_parser = function(data) {
-        var i, len, selector, args;
-        len = data.length;
-        for (i = 0; i < len; i++) {
-            // check for end of command:
-            if (data[i] === String.fromCharCode(7)) { // vertical tab
-                // we have the next_command...
-                // Turn newlines into backslash + "n" so
-                // eval will do the right thing
-                next_command = next_command.replace(/\n/g, "\\n");
-                selector = next_command.slice(0, next_command.indexOf(" "));
-                args = next_command.slice(selector.length + 1);
-                next_command = "";
-                // Now evaluate it-- unfortunately the V8 engine can't
-                // optimize this eval call.
-                //post("Evaling: " + selector + "(" + args + ");");
-                eval(selector + "(" + args + ");");
-            } else {
-                next_command += data[i];
-            }
-        }
+function init_socket_events () {
+    // A not-quite-FUDI command: selector arg1,arg2,etc. These are
+    // formatted on the C side to be easy to parse here in javascript
+    var command_buffer = {
+        next_command: ""
     };
-
-    connection.on("data", perfect_parser);
-
+    connection.on("data", function(data) {
+        perfect_parser(data, command_buffer);
+    });
     connection.on("error", function(e) {
         console.log("Socket error: " + e.code);
         nw_app_quit();
diff --git a/pd/src/m_glob.c b/pd/src/m_glob.c
index d5d411eb7a05fbf4c18ff83c3084007136bcde99..734084845ebbe3d067162229679a0693632655e7 100644
--- a/pd/src/m_glob.c
+++ b/pd/src/m_glob.c
@@ -36,6 +36,7 @@ void glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv);
 void glob_ping(t_pd *dummy);
 void glob_watchdog(t_pd *dummy);
 void glob_savepreferences(t_pd *dummy);
+void glob_forward_files_from_secondary_instance(void);
 
 void alsa_resync( void);
 
@@ -111,6 +112,9 @@ void glob_init(void)
         CLASS_DEFAULT, A_NULL);
     class_addmethod(glob_pdobject, (t_method)glob_initfromgui, gensym("init"),
         A_GIMME, 0);
+    class_addmethod(glob_pdobject,
+        (t_method)glob_forward_files_from_secondary_instance,
+        gensym("forward_files_from_secondary_instance"), 0);
     class_addmethod(glob_pdobject, (t_method)glob_setfilename, 
         gensym("filename"), A_SYMBOL, A_SYMBOL, 0);
     class_addmethod(glob_pdobject, (t_method)glob_evalfile, gensym("open"),
diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c
index 37d6ac0b69dd805037205f5e0c19dcfa27596adc..1d6d23c1f4d2eee44024e916ac2a2e98c3101694 100644
--- a/pd/src/s_inter.c
+++ b/pd/src/s_inter.c
@@ -1134,12 +1134,22 @@ static int defaultfontshit[MAXFONTS] = {
 int sys_startgui(const char *guidir)
 {
     pid_t childpid;
+    char cwd[FILENAME_MAX];
     char cmdbuf[4*MAXPDSTRING];
     struct sockaddr_in server = {0};
     int len = sizeof(server);
     int ntry = 0, portno = FIRSTPORTNUM;
     int xsock = -1;
     stderr_isatty = isatty(2);
+#ifdef MSW
+        if (GetCurrentDirectory(FILENAME_MAX, cwd) == 0)
+            strcpy(cwd, ".");
+#endif
+#ifdef HAVE_UNISTD_H
+        if (!getcwd(cwd, FILENAME_MAX))
+            strcpy(cwd, ".");
+
+#endif
 #ifdef MSW
     short version = MAKEWORD(2, 0);
     WSADATA nobby;
@@ -1178,15 +1188,7 @@ int sys_startgui(const char *guidir)
             skip starting the GUI up. */
         t_atom zz[NDEFAULTFONT+2];
         int i;
-#ifdef MSW
-        if (GetCurrentDirectory(MAXPDSTRING, cmdbuf) == 0)
-            strcpy(cmdbuf, ".");
-#endif
-#ifdef HAVE_UNISTD_H
-        if (!getcwd(cmdbuf, MAXPDSTRING))
-            strcpy(cmdbuf, ".");
-        
-#endif
+        strcpy(cmdbuf, cwd);
         SETSYMBOL(zz, gensym(cmdbuf));
         for (i = 0; i < (int)NDEFAULTFONT; i++)
             SETFLOAT(zz+i+1, defaultfontshit[i]);
@@ -1358,13 +1360,14 @@ fprintf(stderr, "guidir is %s\n", guidir);
                we add it again as the last argument to make sure we can fetch
                it on the GUI side. */
             sprintf(cmdbuf,
-                "%s/nw/nw %s %d localhost %s %s\n",
+                "%s/nw/nw %s %d localhost %s %s x%.6lx",
                 guidir,
                 guidir,
 //                "/home/user/purr-data/pd/nw",
                 portno,
                 (sys_k12_mode ? "pd-l2ork-k12" : "pd-l2ork"),
-                guidir);
+                guidir,
+                (long unsigned int)pd_this);
 
 #endif
             sys_guicmd = cmdbuf;
@@ -1596,6 +1599,7 @@ fprintf(stderr, "guidir is %s\n", guidir);
         gui_end_array();
 
         gui_end_vmess();
+        gui_vmess("gui_set_cwd", "xs", 0, cwd);
         binbuf_free(aapis);
         binbuf_free(mapis);
     }
diff --git a/pd/src/s_main.c b/pd/src/s_main.c
index c2ef2431f870eaadfcb8f4146954391d428b1be8..bacdd8d2f88033a78f977fcb3ffe30f9cd04afa4 100644
--- a/pd/src/s_main.c
+++ b/pd/src/s_main.c
@@ -259,6 +259,31 @@ static void pd_makeversion(void)
     pd_version = strdup(foo);
 }
 
+/* send the openlist to the GUI before closing a secondary
+   instance of Pd. */
+void glob_forward_files_from_secondary_instance(void)
+{
+        /* check if we are unique, otherwise, just focus existing
+           instance, and if necessary open file inside it. This doesn't
+           yet work with the new GUI because we need to set it up to
+           allow multiple instances. */
+    gui_start_vmess("gui_open_via_unique", "xi", pd_this, sys_unique);
+    gui_start_array();
+    if (sys_openlist)
+    {
+        // send the files to be opened to the GUI. We send them as an
+        // array here so that we don't have to allocate anything here
+        // (as the previous API did)
+        t_namelist *nl;
+        for (nl = sys_openlist; nl; nl = nl->nl_next)
+        {
+            gui_s(nl->nl_string);
+        }
+    }
+    gui_end_array();
+    gui_end_vmess();
+}
+
 /* this is called from main() in s_entry.c */
 int sys_main(int argc, char **argv)
 {
@@ -296,22 +321,6 @@ int sys_main(int argc, char **argv)
         /* send the name of the gui preset */
     gui_vmess("gui_set_gui_preset", "s", sys_gui_preset->s_name);
 
-    if (sys_openlist)
-    {
-        // send the files to be opened to the GUI. We send them one
-        // at a time and let the GUI accumulate them so that we don't
-        // have to allocate anything here (as the previous API did)
-        t_namelist *nl;
-        for (nl = sys_openlist; nl; nl = nl->nl_next)
-        {
-            gui_vmess("gui_build_filelist", "s", nl->nl_string);
-        }
-    }
-        /* check if we are unique, otherwise, just focus existing
-           instance, and if necessary open file inside it. This doesn't
-           yet work with the new GUI because we need to set it up to
-           allow multiple instances. */
-    gui_vmess("gui_check_unique", "i", sys_unique);
     if (sys_externalschedlib)
         return (sys_run_scheduler(sys_externalschedlibname,
             sys_extraflagsstring));