diff --git a/pd/nw/dialog_search.html b/pd/nw/dialog_search.html
index d3534adb89595ec19bf4c47e5863b1c1d1fd8453..74a7e95ef33e3008c762a833fe7c2e2a6f673fab 100644
--- a/pd/nw/dialog_search.html
+++ b/pd/nw/dialog_search.html
@@ -387,8 +387,9 @@ function display_toc() {
 		header.appendChild(a);
 		div.appendChild(header);
 		if (doc.description) {
-		    text_node = document.createTextNode(doc.description);
-		    div.appendChild(text_node);
+		    var p_desc = document.createElement("p");
+		    p_desc.innerHTML = doc.description;
+		    div.appendChild(p_desc);
 		}
 	    } catch (e) {
 		// entry doesn't exist, ignore it
@@ -819,8 +820,7 @@ function display_doc(doc) {
     var div = document.createElement("div"),
         a = document.createElement("a"),
         results_elem = document.getElementById("results"),
-        header,
-        text_node;
+        header;
     a.href = "javascript: pdgui.doc_open('" +
          pdgui.defunkify_windows_path(path.dirname(doc.id)) + "', '" +
          pdgui.defunkify_windows_path(path.basename(doc.id)) + "');"
@@ -831,8 +831,27 @@ function display_doc(doc) {
     header.appendChild(a);
     div.appendChild(header);
     if (doc.description) {
-	text_node = document.createTextNode(doc.description);
-	div.appendChild(text_node);
+        var p_desc = document.createElement("p");
+        p_desc.innerHTML = doc.description;
+	    div.appendChild(p_desc);
+    }
+    if (doc.related_objects) {
+        var p_rel_objs = document.createElement("p");
+        var ref_rel_objs = doc.ref_related_objects.split("\,");
+        p_rel_objs.innerHTML = "Related objects: ".bold();
+        doc.related_objects.split(" ").forEach(function (rel_obj, i, a) {
+            let link_rel_obj = rel_obj;
+            // for the objects with a help patch, add a link to its -help.pd
+            if (ref_rel_objs[i]) {
+                let ref_rel_obj = "javascript: pdgui.doc_open('" +
+                      pdgui.defunkify_windows_path(path.dirname(ref_rel_objs[i])) + "', '" +
+                      pdgui.defunkify_windows_path(path.basename(ref_rel_objs[i])) + "');";
+                link_rel_obj = rel_obj.link(ref_rel_obj);
+            }
+            p_rel_objs.innerHTML += " " + link_rel_obj;
+        });
+        p_rel_objs.style.marginTop = "-10px";
+        div.appendChild(p_rel_objs);
     }
     results_elem.appendChild(div);
     toc_bookmark_update(current_dir);
@@ -866,7 +885,8 @@ function doc_search() {
         fields: {
             title: {boost: 64, expand: true},
             keywords: {boost: 8, expand: true},
-            description: {boost: 1}
+            description: {boost: 2},
+            related_objects: {boost: 1, expand: true}
         }
     });
     // Increase priority of pd-l2ork docs over externals docs
diff --git a/pd/nw/pdgui.js b/pd/nw/pdgui.js
index 11317419bc53b90cfb1a2ab03078a819b590058a..24fbd7e2f987c1505b49123d804fd74015946e4b 100644
--- a/pd/nw/pdgui.js
+++ b/pd/nw/pdgui.js
@@ -103,6 +103,7 @@ var fs = require("fs");
 var path = require("path");
 var dive = require("./dive.js"); // small module to recursively search dirs
 var elasticlunr = require("./elasticlunr.js"); // lightweight full-text search engine in JavaScript, cf. https://github.com/weixsong/elasticlunr.js/
+elasticlunr.clearStopWords();
 
 function init_elasticlunr()
 {
@@ -110,8 +111,8 @@ function init_elasticlunr()
     index.addField("title");
     index.addField("keywords");
     index.addField("description");
-    //index.addField("body");
-    index.addField("path");
+    index.addField("related_objects");
+    index.addField("ref_related_objects");
     index.setRef("id");
     return index;
 }
@@ -129,52 +130,118 @@ function index_entry_esc(s) {
     }
 }
 
-function add_doc_to_index(filename, data) {
-    var title = path.basename(filename, ".pd"),
+// GB: This actually retrieves the meta data concerning related_objects,
+// keywords, and description of help patches.
+function add_doc_details_to_index(filename, data) {
+    var title = path.basename(filename, "-help.pd"),
         big_line = data.replace("\n", " "),
         keywords,
-        desc;
-        // We use [\s\S] to match across multiple lines...
-        keywords = big_line
-            .match(/#X text \-?[0-9]+ \-?[0-9]+ KEYWORDS ([\s\S]*?);/i),
-        desc = big_line
-            .match(/#X text \-?[0-9]+ \-?[0-9]+ DESCRIPTION ([\s\S]*?);/i);
-        keywords = keywords && keywords.length > 1 ? keywords[1].trim() : null;
-        desc = desc && desc.length > 1 ? desc[1].trim() : null;
-        // Remove the Pd escapes for commas
-        desc = desc ? desc.replace(" \\,", ",") : null;
-        if (desc) {
-            // format Pd's "comma atoms" as normal commas
-            desc = desc.replace(" \\,", ",");
-        }
-    if (title.slice(-5) === "-help") {
-        title = title.slice(0, -5);
+        desc,
+        rel_objs,
+        ref_rel_objs;
+    // GB: investigate the related objects of each file
+    const machine_related_objects = {
+        found_objects: [],
+        ref_found_objs: [],
+        state: 'SEARCHING_CANVAS',
+        transitions: {
+            SEARCHING_CANVAS: {
+                search_regex: function(line_string) {
+                    if (/#N canvas \-?\d+ \-?\d+ \-?\d+ \-?\d+ Related_objects \-?\d+;/i.test(line_string)) {
+                        this.state = "SEARCHING_OBJS";
+                    }
+                },
+            },
+            SEARCHING_OBJS: {
+                search_regex: function(line_search) {
+                    var rel_obj = line_search.match(/#X obj \-?\d+ \-?\d+ (pddp\/helplink )?(\w*\/)?([\w|\~]+);/i);
+                    if (rel_obj != null) {
+                        this.adds(rel_obj[3]);
+                    } else if (/#X restore \-?\d+ \-?\d+ pd Related_objects;/i.test(line_search)) {
+                        this.state = "SEARCHING_CANVAS";
+                    } else if ((/#N canvas \-?\d+ \-?\d+ \-?\d+ \-?\d+ [\S]* \-?\d+;/i.test(line_search))) {
+                        if (title != "random") {
+                            this.adds("pd");
+                        }
+                        this.state = "PD_AS_REL_OBJ";
+                    }
+                },
+            },
+            PD_AS_REL_OBJ: {
+                search_regex: function(line_search) {
+                    if (/#X restore \-?\d+ \-?\d+ pd;/i.test(line_search)) {
+                        this.state = "SEARCHING_OBJS";
+                    }
+                }
+            }
+        },
+        dispatch(actionName, line_string) {
+            const action = this.transitions[this.state][actionName];
+            if (action) {
+                action.call(this, line_string);
+            }
+        },
+        adds(obj_title) {
+            if (!this.found_objects.includes(obj_title)) {
+                let obj_path = index.search(obj_title, {fields: {title: {}}});
+                if (obj_path.length > 0) obj_path = obj_path[0].ref;
+                this.found_objects.push(obj_title);
+                this.ref_found_objs.push(obj_path);
+            }
+        },
+    };
+    rel_objs = Object.create(machine_related_objects);
+    let eval_string = data.split("\n");
+    eval_string.forEach(function(l,a,i) {
+        rel_objs.dispatch('search_regex', l.toString().replace(/\(/g, "\\\(")
+            .replace(/\)/, "\\\)"));
+    });
+    ref_rel_objs = rel_objs.ref_found_objs.toString();
+    rel_objs = rel_objs.found_objects;
+    rel_objs = rel_objs ? rel_objs.toString().replace(/\,/g, " ") : null;
+
+    // We use [\s\S] to match across multiple lines...
+    keywords = big_line
+        .match(/#X text \-?[0-9]+ \-?[0-9]+ KEYWORDS ([\s\S]*?);/i);
+    desc = big_line
+        .match(/#X text \-?[0-9]+ \-?[0-9]+ DESCRIPTION ([\s\S]*?);/i);
+    keywords = keywords && keywords.length > 1 ? keywords[1].trim() : null;
+    desc = desc && desc.length > 1 ? desc[1].trim() : null;
+    // Remove the Pd escapes for commas
+    desc = desc ? desc.replace(" \\,", ",") : null;
+    if (desc) {
+        // format Pd's "comma atoms" as normal commas
+        desc = desc.replace(" \\,", ",");
     }
-    index_cache[index_cache.length] = [filename, title, keywords, desc]
+
+    index_cache[index_cache.length] = [filename, title, keywords, desc, rel_objs, ref_rel_objs]
         .map(index_entry_esc).join(":");
     var d = path.dirname(filename);
     index_manif.add(d);
     // Also add the parent directory to catch additions of siblings.
     index_manif.add(path.dirname(d));
-    index.addDoc({
+    index.update({
         "id": filename,
         "title": title,
         "keywords": keywords,
-        "description": desc
+        "description": desc,
+        "related_objects": rel_objs,
+        "ref_related_objects": ref_rel_objs
         //"body": big_line,
     });
 }
 
-function read_file(err, filename, stat) {
+// GB: This does an initial scan of help patches, recording filename, title and
+// parent dir, without looking at the meta data.
+function add_doc_to_index(err, filename, stat) {
     if (!err) {
-        if (filename.slice(-3) === ".pd") {
-            // AG: We MUST read the files synchronously here. This might be a
-            // performance issue on some systems, but if we don't do this then
-            // we may open a huge number of files simultaneously, causing the
-            // process to run out of file handles.
+        if (filename.slice(-8) === "-help.pd") {
             try {
-                var data = fs.readFileSync(filename, { encoding: "utf8", flag: "r" });
-                add_doc_to_index(filename, data);
+                let title = path.basename(filename, "-help.pd");
+                index.addDoc({
+                    "id": filename,
+                    "title": title
+                })
             } catch (read_err) {
                 post("err: " + read_err);
             }
@@ -269,22 +336,42 @@ function make_index() {
     var doc_path = browser_doc?path.join(lib_dir, "doc"):lib_dir;
     var i = 0;
     var l = help_path.length;
+    function detail_files() {
+        let all_indexed_files = Object.keys(index.documentStore.docs);
+        var data;
+        all_indexed_files.forEach(function(filename,i,a) {
+            // AG: We MUST read the files synchronously here. This might be a
+            // performance issue on some systems, but if we don't do this then
+            // we may open a huge number of files simultaneously, causing the
+            // process to run out of file handles.
+            try {
+                data = fs.readFileSync(filename, { encoding: "utf8", flag: "r" });
+                add_doc_details_to_index(filename, data);
+            } catch (read_err) {
+                post("err: " + read_err);
+            }
+        });
+        finish_index();
+    }
     function make_index_cont() {
-        if (i < l) {
+        if (browser_path && i < l) {
             var doc_path = help_path[i++];
             // AG: These paths might not exist, ignore them in this case. Also
             // note that we need to expand ~ here.
             var full_path = expand_tilde(doc_path);
             fs.lstat(full_path, function(err, stat) {
                 if (!err) {
-                    post("building help index in " + doc_path);
-                    dive(full_path, read_file, make_index_cont);
+                    post("scanning help patches in " + doc_path);
+                    dive(full_path, add_doc_to_index, make_index_cont);
                 } else {
                     make_index_cont();
                 }
             });
         } else {
-            finish_index();
+            // reset the help path index, then invoke the main pass
+            i = 0;
+            post("building help index");
+            detail_files();
         }
     }
     pdsend("pd gui-busy 1");
@@ -314,12 +401,16 @@ function make_index() {
                 var filename = e[0] ? e[0] : null;
                 var title = e[1] ? e[1] : null;
                 var keywords = e[2] ? e[2] : null;
-                var descr = e[3] ? e[3] : null;
+                var desc = e[3] ? e[3] : null;
+                var rel_obj = e[4] ? e[4] : null;
+                var ref_rel_obj = e[5] ? e[5] : null;
                 index.addDoc({
                     "id": filename,
                     "title": title,
                     "keywords": keywords,
-                    "description": descr
+                    "description": desc,
+                    "related_objects": rel_obj,
+                    "ref_related_objects": ref_rel_obj
                 });
             }
         }
@@ -327,8 +418,8 @@ function make_index() {
     } else {
         // no index cache, or it is out of date, so (re)build it now, and
         // save the new cache along the way
-        post("building help index in " + doc_path);
-        dive(doc_path, read_file, browser_path?make_index_cont:finish_index);
+        post("scanning help patches in " + doc_path);
+        dive(doc_path, add_doc_to_index, make_index_cont);
     }
     pdsend("pd gui-busy 0");
 }