diff --git a/package-lock.json b/package-lock.json
index d9d512153e883e783f23fc9eac133d33e96fba4b..e2cbdb81a8810a2dd120b265a1d13504d701bbf6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -133,6 +133,14 @@
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
       "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg=="
     },
+    "axios": {
+      "version": "0.20.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz",
+      "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==",
+      "requires": {
+        "follow-redirects": "^1.10.0"
+      }
+    },
     "babel-runtime": {
       "version": "6.26.0",
       "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
@@ -983,6 +991,11 @@
         "unpipe": "~1.0.0"
       }
     },
+    "follow-redirects": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
+      "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
+    },
     "for-in": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
diff --git a/package.json b/package.json
index fdeb416aabbedda9c2d6bbba0c22aca7b6e4a6d5..bb0f7cf2a3a89d35d18f500cb53218bde872ad4e 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
     "test": ""
   },
   "dependencies": {
+    "axios": "^0.20.0",
     "body-parser": "^1.19.0",
     "compression": "^1.7.4",
     "cookie-parser": "1.4.3",
diff --git a/routes/routes-project.js b/routes/routes-project.js
index a7a5389c06a4246febf54ce92309e08b00e0d6dc..89775cb3fc31da0fe799459ebf14a2b976863dc0 100644
--- a/routes/routes-project.js
+++ b/routes/routes-project.js
@@ -1,5 +1,5 @@
 const fs = require('fs')
-const SamlStrategy = require('passport-saml').Strategy
+//const SamlStrategy = require('passport-saml').Strategy
 const dbconn = require('./dbconn')
 const methods = require('./methods')
 // pwd encryption
@@ -13,6 +13,7 @@ const async = require('async')
 
 const helpers = require('./helpers')
 const pictSizeLimit = 1000000 // 1 MB
+const axios = require('axios')
 
 module.exports = function (app) {
  
@@ -355,4 +356,82 @@ module.exports = function (app) {
     ])
   })
 
+  async function getProjectsFromGitlab(perPage, idAfter) {
+    // public projects
+    return await axios.get('https://transfer.hft-stuttgart.de/gitlab/api/v4/projects?visibility=public&pagination=keyset&per_page='+
+      perPage+'&order_by=id&sort=asc&id_after='+idAfter)
+  }
+
+  app.get('/projectlist', async function(req, res){
+    let projectArr = []
+    let websiteArr = []
+    let isProject = true
+    let firstId = 0
+    let webname = "";
+
+    while (isProject == true) {
+      let projects = await getProjectsFromGitlab(10, firstId)
+      let projectData = projects.data
+
+      if (projectData.length == 0) {
+        isProject = false
+      }
+      else {
+        for(let i = 0; i < projectData.length; i++){
+          // skip template project
+          if (projectData[i].name == "template_gitlab_page") {
+            continue
+          }
+
+          // M4_LAB logo for all projects that do not have logo
+          if (projectData[i].avatar_url == null) {
+            projectData[i].avatar_url = "https://m4lab.hft-stuttgart.de/img/footer/M4_LAB_LOGO_Graustufen.png"
+          }
+         
+          // websites
+          if (projectData[i].tag_list.includes('website')) {
+            // customize website name
+            if (projectData[i].name == "Visualization") {
+              webname = "https://transfer.hft-stuttgart.de/pages/visualization"
+            }
+            else if (projectData[i].name == "IN-Source") {
+              webname = "https://transfer.hft-stuttgart.de/pages/INsource"
+            }
+            else if (projectData[i].name == "3DS_Visualization_Cesium") {
+              webname = "https://transfer.hft-stuttgart.de/pages/3ds_visualization_cesium"
+            }
+            else {
+              webname = "https://transfer.hft-stuttgart.de/pages/"+projectData[i].name
+            }
+            let website = {
+              logo: projectData[i].avatar_url,
+              name: projectData[i].name,
+              weburl: webname,
+              desc: projectData[i].description
+            }
+            websiteArr.push(website)
+          }
+          // project repo
+          else {
+            let project = {
+              logo: projectData[i].avatar_url,
+              name: projectData[i].name,
+              weburl: projectData[i].web_url,
+              desc: projectData[i].description
+            }
+            projectArr.push(project)
+          }
+        }
+
+        firstId = projectData[projectData.length-1].id
+      }
+    }
+
+    res.render(lang+'/project/projectList', {
+      project: projectArr,
+      website: websiteArr
+    })
+  })
+
+
 };
\ No newline at end of file
diff --git a/views/DE/project/projectList.pug b/views/DE/project/projectList.pug
new file mode 100644
index 0000000000000000000000000000000000000000..b21a0b0b4bc30a3aa0fd6b896b91d43f012b7038
--- /dev/null
+++ b/views/DE/project/projectList.pug
@@ -0,0 +1,69 @@
+doctype html
+html(lang="de")
+  head
+    title= "Project List"
+    meta(charset="UTF-8")
+    meta(name="viewport", content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no")
+    link(rel="stylesheet", type="text/css", href="https://transfer.hft-stuttgart.de/css/bootstrap.min.css")
+    link(rel="stylesheet", type="text/css", href="https://transfer.hft-stuttgart.de/css/m4lab.css")
+    link(rel="stylesheet", href="https://use.fontawesome.com/releases/v5.8.2/css/all.css", integrity="sha384-oS3vJWv+0UjzBfQzYUhtDYW+Pj2yciDJxpsK1OYPAYjqT085Qq/1cq5FLXAZQ7Ay", crossorigin="anonymous")
+
+  body
+    div(class="container")
+        div(class="pt-4 pb-4")
+            input(id="searchInput", class="form-control form-control-dark w-100", type="text", placeholder="Suchen Sie hier nach Themen und Projekten", onkeyup="searchFunction()")
+        h3(class="mb-3 font-weight-bold") Projekte
+        table(class="table table-striped")
+            tbody
+                for item in project
+                    tr
+                        td
+                            img(src=item.logo, width="40", height="40")
+                        td <a href="#{item.weburl}" target="_blank">#{item.name}</a>
+                        td #{item.desc}
+
+        if website.length > 0
+            h3(class="mb-3 font-weight-bold") Websites
+            table(class="table table-striped")
+                for item in website
+                    tr
+                        td
+                            img(src=item.logo, width="40", height="40")
+                        td <a href="#{item.weburl}" target="_blank">#{item.name}</a>
+                        td #{item.desc}
+
+    // jQuery
+    script(src="https://code.jquery.com/jquery-3.3.1.min.js")
+    script(src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js", integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1", crossorigin="anonymous")
+    // Bootstrap
+    script(src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous")
+    // Header
+    script(src="https://transfer.hft-stuttgart.de/js/headfoot.js")
+    script.
+        function searchFunction() {
+            var input, filter, rows, col, txtValue;
+            var isFound = true;
+            input = document.getElementById("searchInput");
+            filter = input.value.toUpperCase();
+            
+            rows = document.getElementsByTagName("tr");
+            for (i = 0; i < rows.length; i++) {
+                cols = rows[i].getElementsByTagName("td");
+                // check all cos
+                for (j = 0; j < cols.length; j++) {
+                    txtValue = cols[j].textContent || cols[j].innerText;
+                    if (txtValue.toUpperCase().indexOf(filter) > -1) {
+                        isFound = true;
+                        break;
+                    } else {
+                        isFound = false;
+                    }
+                }
+                if (isFound) {
+                    rows[i].style.display = "block";
+                }
+                else {
+                    rows[i].style.display = "none";
+                }
+            }
+        }
\ No newline at end of file