{"id":23949697,"url":"https://github.com/liaplayground/robolab2024","last_synced_at":"2026-03-05T14:30:53.643Z","repository":{"id":222668580,"uuid":"758044275","full_name":"LiaPlayground/RoboLab2024","owner":"LiaPlayground","description":"Vorstellung des RoboLabs und der genutzten Browser Technologien in Freiberg","archived":false,"fork":false,"pushed_at":"2024-02-15T15:00:21.000Z","size":593,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-24T08:47:24.825Z","etag":null,"topics":["liascript","liascript-course","oer","remote-lab"],"latest_commit_sha":null,"homepage":"https://liascript.github.io/course/?https://raw.githubusercontent.com/LiaPlayground/RoboLab2024/main/README.md","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"cc0-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LiaPlayground.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2024-02-15T14:13:36.000Z","updated_at":"2025-02-10T09:34:29.000Z","dependencies_parsed_at":"2024-02-15T15:51:55.607Z","dependency_job_id":null,"html_url":"https://github.com/LiaPlayground/RoboLab2024","commit_stats":null,"previous_names":["liaplayground/robolab2024"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/LiaPlayground/RoboLab2024","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaPlayground%2FRoboLab2024","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaPlayground%2FRoboLab2024/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaPlayground%2FRoboLab2024/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaPlayground%2FRoboLab2024/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiaPlayground","download_url":"https://codeload.github.com/LiaPlayground/RoboLab2024/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiaPlayground%2FRoboLab2024/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30130274,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T12:40:50.676Z","status":"ssl_error","status_checked_at":"2026-03-05T12:39:32.209Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["liascript","liascript-course","oer","remote-lab"],"created_at":"2025-01-06T11:49:52.774Z","updated_at":"2026-03-05T14:30:53.623Z","avatar_url":"https://github.com/LiaPlayground.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--\n\nauthor: André Dietrich\n\n@embed: \u003ciframe src=\"https://api.allorigins.win/raw?url=@0\" style=\"width: 100%; height: 60vh;\"\u003e\u003c/iframe\u003e\n\nversion:  1.0.0\n\nlanguage: de\n\nnarrator: Deutsch Female\n\nimport: https://raw.githubusercontent.com/liaTemplates/ABCjs/main/README.md\n\nimport: https://raw.githubusercontent.com/liaTemplates/vtk/master/README.md\n\nimport: https://raw.githubusercontent.com/LiaTemplates/DigiSim/master/README.md\n\n--\u003e\n\n\n# Dein Browser das unbekannte Wesen\n\n![Mystical Browser](6f7a68aa4068b8d982108688fbf7f8c554686d16.webp)\n\n## Internet\n\n\u003cdiv style=\"width:100%;height:0;padding-bottom:76%;position:relative;\"\u003e\u003ciframe src=\"https://giphy.com/embed/fxO5urhnGImBuYtph7\" width=\"100%\" height=\"100%\" style=\"position:absolute\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/internet-cyberspace-surfing-the-net-fxO5urhnGImBuYtph7\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n\n### Ursprung - ARPANET\n\n![ARPA-Net](https://upload.wikimedia.org/wikipedia/commons/b/bc/Arpanet_map_1973.jpg \"Karte des ARPA-Netzwerks im Jahre 1973\")\n\n### World Wide Web\n\n![Tim Burners-Lee](https://upload.wikimedia.org/wikipedia/commons/9/9d/Sir_Tim_Berners-Lee.jpg \"Tim Berners-Lee 2014\")\n\n    {{1}}\n![Vague but exiting](https://info.cern.ch/images/proposal.gif \"Im März 1989 reichte Tim Berners-Lee einen Vorschlag für ein Informationsverwaltungssystem bei seinem Chef, Mike Sendall, ein. „Vage, aber aufregend“, waren die Worte, die Sendall auf den Vorschlag schrieb.\")\n\n\n    {{2}}\n\u003csection\u003e\n\nBis Weihnachten 1990 hatte Berners-Lee alle erforderlichen Werkzeuge für ein funktionierendes Web entwickelt:\n\n* das HyperText Transfer Protocol (HTTP) 0.9\n* die HyperText Markup Language (HTML)\n* den ersten Webbrowser (der auch ein HTML-Editor war und auf Usenet-Newsgroups und FTP-Dateien zugreifen konnte)\n\n      {{3}}\n  ![WWW Browser](https://www.w3.org/MarkUp/tims_editor \"_Quelle: https://www.w3.org/People/Berners-Lee/WorldWideWeb.html - Erster Browser / Editor_\")\n\n* die erste HTTP-Server-Software (später bekannt als CERN httpd)\n* den ersten Webserver\n* und die ersten Webseiten, die das Projekt selbst beschrieben\n\n\u003c/section\u003e\n\n    {{4}}\n\u003e Die Stanford Federal Credit Union war im Oktober 1994 die erste Finanzinstitution, die ihren Mitgliedern Online-Banking-Dienste über das Internet anbot.\n\n    {{5}}\n\u003e Bis 1995 war das Internet in den USA vollständig kommerzialisiert, als das NSFNet außer Betrieb genommen wurde und damit die letzten Beschränkungen für die Nutzung des Internets zur Beförderung von kommerziellem Verkehr aufgehoben wurden.\n\n\n### Das Internet heute\n\n![Karte des Internets](https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/13ad2603-668b-4c11-a371-91e56d25998d/deihd1a-6661e755-7deb-4fd6-8312-71c00c1911e2.jpg?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOjdlMGQxODg5ODIyNjQzNzNhNWYwZDQxNWVhMGQyNmUwIiwiaXNzIjoidXJuOmFwcDo3ZTBkMTg4OTgyMjY0MzczYTVmMGQ0MTVlYTBkMjZlMCIsIm9iaiI6W1t7InBhdGgiOiJcL2ZcLzEzYWQyNjAzLTY2OGItNGMxMS1hMzcxLTkxZTU2ZDI1OTk4ZFwvZGVpaGQxYS02NjYxZTc1NS03ZGViLTRmZDYtODMxMi03MWMwMGMxOTExZTIuanBnIn1dXSwiYXVkIjpbInVybjpzZXJ2aWNlOmZpbGUuZG93bmxvYWQiXX0.mcMHkLQvrzT2hs3CvUWgAwQx7at8DxSEDWX1m6QVHn0 \"_Quelle: https://www.halcyonmaps.com/map-of-the-internet-2021 _\")\n\n    {{1}}\n[The Internet Map](http://internet-map.net)\n\n\n## JavaScript\n\n![Foto Brendan Eich](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Brendan_Eich_Mozilla_Foundation_official_photo.jpg/768px-Brendan_Eich_Mozilla_Foundation_official_photo.jpg \"_Brendan Eich 2012 - Der Erfinder von JavaScript_\")\n\n\n\u003e JavaScript ist eine ___high-level___, oft ___just-in-time kompilierte___ Sprache, die dem ECMAScript-Standard entspricht. Sie verfügt über ___dynamische Typisierung___, ___prototypenbasierte Objektorientierung___ und ___Funktionen als Erstklassige Objekte___. Sie ist ___mehrparadigmatisch___ und unterstützt ___ereignisgesteuerte___, ___funktionale___ und ___imperative Programmierungsstile___. Sie verfügt über Anwendungsprogrammierschnittstellen (APIs) zum Arbeiten mit Text, Datum, regulären Ausdrücken, standardmäßigen Datenstrukturen und dem Dokumentenobjektmodell (DOM).\n\u003e\n\u003e Quelle: [Wikipedia](https://de.wikipedia.org/wiki/JavaScript)\n\n    {{1}}\n__September 1995:__\nAls Betaversion veröffentlicht im Netscape Navigator\n\n    {{2}}\n__August 1996:__\nVeröffentlichung von JScript im Microsoft Internet Explorer 3.0 _(Reverse-Engineered von Netscape)_\n\n    {{3}}\n__November 1996:__\nNetscape reichte JavaScript bei Ecma International ein, als Ausgangspunkt für eine Standard-Spezifikation\n\n    {{4}}\n__Juni 1997:__\nOffizielle Veröffentlichung der ersten ECMAScript-Sprachspezifikation\n \n\n### Verbreitung\n\n\u003c!-- data-show\ndata-type=\"barchart\"\ndata-title=\"Verbreitung von Programmiersprachen bei Entwicklern\"--\u003e\n| Programmiersprache      | Anteil in % |\n| ----------------------- | -----------:|\n| JavaScript              |       63.61 |\n| HTML/CSS                |       52.97 |\n| Python                  |       49.28 |\n| SQL                     |       48.66 |\n| TypeScript              |       38.87 |\n| Bash/Shell (all shells) |       32.37 |\n| Java                    |       30.55 |\n| C#                      |       27.62 |\n| C++                     |       22.42 |\n| C                       |       19.34 |\n| PHP                     |       18.58 |\n| PowerShell              |       13.59 |\n| Go                      |       13.24 |\n| Rust                    |       13.05 |\n| Kotlin                  |        9.06 |\n| Ruby                    |        6.23 |\n| Lua                     |        6.09 |\n| Dart                    |        6.02 |\n| Assembly                |        5.43 |\n| Swift                   |        4.65 |\n| R                       |        4.23 |\n| Visual Basic (.Net)     |        4.07 |\n| MATLAB                  |        3.81 |\n| VBA                     |        3.55 |\n| Groovy                  |        3.40 |\n| Delphi                  |        3.23 |\n| Scala                   |        2.77 |\n| Perl                    |        2.46 |\n| Elixir                  |        2.32 |\n| Objective-C             |        2.31 |\n| Haskell                 |        2.09 |\n| GDScript                |        1.71 |\n| Lisp                    |        1.53 |\n| Solidity                |        1.33 |\n| Clojure                 |        1.26 |\n| Julia                   |        1.15 |\n| Erlang                  |        0.99 |\n| F#                      |        0.97 |\n| Fortran                 |        0.95 |\n| Prolog                  |        0.89 |\n| Zig                     |        0.83 |\n| Ada                     |        0.77 |\n| OCaml                   |        0.70 |\n| Apex                    |        0.66 |\n| Cobol                   |        0.66 |\n| SAS                     |        0.49 |\n| Crystal                 |        0.44 |\n| Nim                     |        0.38 |\n| APL                     |        0.26 |\n| Flow                    |        0.24 |\n| Raku                    |        0.18 |\n\nQuelle: https://www.statista.com/statistics/793628/worldwide-developer-survey-most-used-languages/\n\n---\n\n    {{1}}\n![Programmiersprachen Popularität](d3487eb81b4cba2070de763ee5cc6a03c83ee433.png \"Quelle: https://octoverse.github.com/2022/top-programming-languages\")\n\n### Taschenrechner\n\nÖffnen der Konsole: \n\u003ckbd\u003eStrg\u003c/kbd\u003e + \u003ckbd\u003eShift\u003c/kbd\u003e + \u003ckbd\u003ei\u003c/kbd\u003e\n\n``` js\nvar x = 22\n2000 + (30.1 * x) ** 3\n```\n\u003cscript\u003e@input\u003c/script\u003e\n\n\n### Sensor API\n\n``` js\nfunction handleOrientation(event) {\n  let alpha = event.alpha; // Z-axis rotation (0-360 degrees)\n  let beta = event.beta;   // Front-back rotation (-180 to  180 degrees)\n  let gamma = event.gamma; // Left-right tilt (-90 to  90 degrees)\n\n  console.log(`Alpha: ${alpha}`);\n  console.log(`Beta: ${beta}`);\n  console.log(`Gamma: ${gamma}`);\n}\n\nwindow.addEventListener('deviceorientation', handleOrientation);\n```\n\u003cscript\u003e\n@input\n\nsend.handle(\"stop\", () =\u003e {\n  window.removeEventListener('deviceorientation', handleOrientation);\n})\n\n\"LIA: terminal\"\n\u003c/script\u003e\n\n#### Demo\n\u003c!--\npersistent: true\n--\u003e\n\n\u003cvideo autoplay=\"false\" id=\"videoElement\" style=\"width: 100%\"\u003e\u003c/video\u003e\n\n\u003cscript\u003e\nvar video = document.querySelector(\"#videoElement\");\n\nif (navigator.mediaDevices.getUserMedia) {\n navigator.mediaDevices.getUserMedia({ video: true })\n    .then(function (stream) {\n      video.srcObject = stream;\n    })\n    .catch(function (err0r) {\n      console.log(\"Something went wrong!\");\n    });\n}\nconsole.log(\"Accessing the cam\")\n\u003c/script\u003e\n\n### Geolocation\n\n``` js\nfunction successCallback(position) {\n  var latitude = position.coords.latitude;\n  var longitude = position.coords.longitude;\n  console.log(\"Latitude: \" + latitude + \", Longitude: \" + longitude);\n}\n\nfunction errorCallback(error) {\n  console.log(\"Error Code = \" + error.code + \" - \" + error.message);\n}\n\nnavigator.geolocation.getCurrentPosition(successCallback, errorCallback);\n```\n\u003cscript\u003e@input\u003c/script\u003e\n\n### Fetching Data\n\n``` js\nfetch(\"https://api.open-meteo.com/v1/forecast?latitude=50.92558\u0026longitude=13.33125\u0026hourly=temperature_2m\")\n    .then(response =\u003e response.json())\n    .then(data =\u003e {\n        let table = \"| Time | Temperature |\\n\"\n        table += \"| ---- | ----------- |\\n\"\n\n        for(let i=0; i \u003c data.hourly.time.length; i++) {\n            table += \"| \" + data.hourly.time[i] + \" | \" + data.hourly.temperature_2m[i] + \" |\\n\"\n        }\n\n        console.log(table)\n    })\n    .catch(e =\u003e {\n        send.lia(\"ups, something went wrong\")\n    })\n\n\"waiting for the weather\"\n```\n\u003cscript\u003e@input\u003c/script\u003e\n\n\n### WebGL\n\n\n``` js\n'use strict';\n\nconst canvas = document.getElementsByTagName('canvas')[0];\ncanvas.width = canvas.clientWidth;\ncanvas.height = canvas.clientHeight;\n\nlet config = {\n    TEXTURE_DOWNSAMPLE: 1,\n    DENSITY_DISSIPATION: 0.98,\n    VELOCITY_DISSIPATION: 0.99,\n    PRESSURE_DISSIPATION: 0.8,\n    PRESSURE_ITERATIONS: 25,\n    CURL: 30,\n    SPLAT_RADIUS: 0.005\n}\n\nlet pointers = [];\nlet splatStack = [];\n\nconst {\n    gl,\n    ext\n} = getWebGLContext(canvas);\n\nfunction getWebGLContext(canvas) {\n    const params = {\n        alpha: false,\n        depth: false,\n        stencil: false,\n        antialias: false\n    };\n\n    let gl = canvas.getContext('webgl2', params);\n    const isWebGL2 = !!gl;\n    if (!isWebGL2)\n        gl = canvas.getContext('webgl', params) || canvas.getContext('experimental-webgl', params);\n\n    let halfFloat;\n    let supportLinearFiltering;\n    if (isWebGL2) {\n        gl.getExtension('EXT_color_buffer_float');\n        supportLinearFiltering = gl.getExtension('OES_texture_float_linear');\n    } else {\n        halfFloat = gl.getExtension('OES_texture_half_float');\n        supportLinearFiltering = gl.getExtension('OES_texture_half_float_linear');\n    }\n\n    gl.clearColor(0.0, 0.0, 0.0, 1.0);\n\n    const halfFloatTexType = isWebGL2 ? gl.HALF_FLOAT : halfFloat.HALF_FLOAT_OES;\n    let formatRGBA;\n    let formatRG;\n    let formatR;\n\n    if (isWebGL2) {\n        formatRGBA = getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, halfFloatTexType);\n        formatRG = getSupportedFormat(gl, gl.RG16F, gl.RG, halfFloatTexType);\n        formatR = getSupportedFormat(gl, gl.R16F, gl.RED, halfFloatTexType);\n    } else {\n        formatRGBA = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n        formatRG = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n        formatR = getSupportedFormat(gl, gl.RGBA, gl.RGBA, halfFloatTexType);\n    }\n\n    return {\n        gl,\n        ext: {\n            formatRGBA,\n            formatRG,\n            formatR,\n            halfFloatTexType,\n            supportLinearFiltering\n        }\n    };\n}\n\nfunction getSupportedFormat(gl, internalFormat, format, type) {\n    if (!supportRenderTextureFormat(gl, internalFormat, format, type)) {\n        switch (internalFormat) {\n            case gl.R16F:\n                return getSupportedFormat(gl, gl.RG16F, gl.RG, type);\n            case gl.RG16F:\n                return getSupportedFormat(gl, gl.RGBA16F, gl.RGBA, type);\n            default:\n                return null;\n        }\n    }\n\n    return {\n        internalFormat,\n        format\n    }\n}\n\nfunction supportRenderTextureFormat(gl, internalFormat, format, type) {\n    let texture = gl.createTexture();\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 4, 4, 0, format, type, null);\n\n    let fbo = gl.createFramebuffer();\n    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n\n    const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n    if (status != gl.FRAMEBUFFER_COMPLETE)\n        return false;\n    return true;\n}\n\nfunction pointerPrototype() {\n    this.id = -1;\n    this.x = 0;\n    this.y = 0;\n    this.dx = 0;\n    this.dy = 0;\n    this.down = false;\n    this.moved = false;\n    this.color = [30, 0, 300];\n}\n\npointers.push(new pointerPrototype());\n\nclass GLProgram {\n    constructor(vertexShader, fragmentShader) {\n        this.uniforms = {};\n        this.program = gl.createProgram();\n\n        gl.attachShader(this.program, vertexShader);\n        gl.attachShader(this.program, fragmentShader);\n        gl.linkProgram(this.program);\n\n        if (!gl.getProgramParameter(this.program, gl.LINK_STATUS))\n            throw gl.getProgramInfoLog(this.program);\n\n        const uniformCount = gl.getProgramParameter(this.program, gl.ACTIVE_UNIFORMS);\n        for (let i = 0; i \u003c uniformCount; i++) {\n            const uniformName = gl.getActiveUniform(this.program, i).name;\n            this.uniforms[uniformName] = gl.getUniformLocation(this.program, uniformName);\n        }\n    }\n\n    bind() {\n        gl.useProgram(this.program);\n    }\n}\n\nfunction compileShader(type, source) {\n    const shader = gl.createShader(type);\n    gl.shaderSource(shader, source);\n    gl.compileShader(shader);\n\n    if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS))\n        throw gl.getShaderInfoLog(shader);\n\n    return shader;\n};\n\nconst baseVertexShader = compileShader(gl.VERTEX_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    attribute vec2 aPosition;\n    varying vec2 vUv;\n    varying vec2 vL;\n    varying vec2 vR;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform vec2 texelSize;\n\n    void main () {\n        vUv = aPosition * 0.5 + 0.5;\n        vL = vUv - vec2(texelSize.x, 0.0);\n        vR = vUv + vec2(texelSize.x, 0.0);\n        vT = vUv + vec2(0.0, texelSize.y);\n        vB = vUv - vec2(0.0, texelSize.y);\n        gl_Position = vec4(aPosition, 0.0, 1.0);\n    }\n`);\n\nconst clearShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    uniform sampler2D uTexture;\n    uniform float value;\n\n    void main () {\n        gl_FragColor = value * texture2D(uTexture, vUv);\n    }\n`);\n\nconst displayShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    uniform sampler2D uTexture;\n\n    void main () {\n        gl_FragColor = texture2D(uTexture, vUv);\n    }\n`);\n\nconst splatShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    uniform sampler2D uTarget;\n    uniform float aspectRatio;\n    uniform vec3 color;\n    uniform vec2 point;\n    uniform float radius;\n\n    void main () {\n        vec2 p = vUv - point.xy;\n        p.x *= aspectRatio;\n        vec3 splat = exp(-dot(p, p) / radius) * color;\n        vec3 base = texture2D(uTarget, vUv).xyz;\n        gl_FragColor = vec4(base + splat, 1.0);\n    }\n`);\n\nconst advectionManualFilteringShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    uniform sampler2D uVelocity;\n    uniform sampler2D uSource;\n    uniform vec2 texelSize;\n    uniform float dt;\n    uniform float dissipation;\n\n    vec4 bilerp (in sampler2D sam, in vec2 p) {\n        vec4 st;\n        st.xy = floor(p - 0.5) + 0.5;\n        st.zw = st.xy + 1.0;\n        vec4 uv = st * texelSize.xyxy;\n        vec4 a = texture2D(sam, uv.xy);\n        vec4 b = texture2D(sam, uv.zy);\n        vec4 c = texture2D(sam, uv.xw);\n        vec4 d = texture2D(sam, uv.zw);\n        vec2 f = p - st.xy;\n        return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n    }\n\n    void main () {\n        vec2 coord = gl_FragCoord.xy - dt * texture2D(uVelocity, vUv).xy;\n        gl_FragColor = dissipation * bilerp(uSource, coord);\n        gl_FragColor.a = 1.0;\n    }\n`);\n\nconst advectionShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    uniform sampler2D uVelocity;\n    uniform sampler2D uSource;\n    uniform vec2 texelSize;\n    uniform float dt;\n    uniform float dissipation;\n\n    void main () {\n        vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;\n        gl_FragColor = dissipation * texture2D(uSource, coord);\n        gl_FragColor.a = 1.0;\n    }\n`);\n\nconst divergenceShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    varying vec2 vL;\n    varying vec2 vR;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform sampler2D uVelocity;\n\n    vec2 sampleVelocity (in vec2 uv) {\n        vec2 multiplier = vec2(1.0, 1.0);\n        if (uv.x \u003c 0.0) { uv.x = 0.0; multiplier.x = -1.0; }\n        if (uv.x \u003e 1.0) { uv.x = 1.0; multiplier.x = -1.0; }\n        if (uv.y \u003c 0.0) { uv.y = 0.0; multiplier.y = -1.0; }\n        if (uv.y \u003e 1.0) { uv.y = 1.0; multiplier.y = -1.0; }\n        return multiplier * texture2D(uVelocity, uv).xy;\n    }\n\n    void main () {\n        float L = sampleVelocity(vL).x;\n        float R = sampleVelocity(vR).x;\n        float T = sampleVelocity(vT).y;\n        float B = sampleVelocity(vB).y;\n        float div = 0.5 * (R - L + T - B);\n        gl_FragColor = vec4(div, 0.0, 0.0, 1.0);\n    }\n`);\n\nconst curlShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    varying vec2 vL;\n    varying vec2 vR;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform sampler2D uVelocity;\n\n    void main () {\n        float L = texture2D(uVelocity, vL).y;\n        float R = texture2D(uVelocity, vR).y;\n        float T = texture2D(uVelocity, vT).x;\n        float B = texture2D(uVelocity, vB).x;\n        float vorticity = R - L - T + B;\n        gl_FragColor = vec4(vorticity, 0.0, 0.0, 1.0);\n    }\n`);\n\nconst vorticityShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform sampler2D uVelocity;\n    uniform sampler2D uCurl;\n    uniform float curl;\n    uniform float dt;\n\n    void main () {\n        float T = texture2D(uCurl, vT).x;\n        float B = texture2D(uCurl, vB).x;\n        float C = texture2D(uCurl, vUv).x;\n        vec2 force = vec2(abs(T) - abs(B), 0.0);\n        force *= 1.0 / length(force + 0.00001) * curl * C;\n        vec2 vel = texture2D(uVelocity, vUv).xy;\n        gl_FragColor = vec4(vel + force * dt, 0.0, 1.0);\n    }\n`);\n\nconst pressureShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    varying vec2 vL;\n    varying vec2 vR;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform sampler2D uPressure;\n    uniform sampler2D uDivergence;\n\n    vec2 boundary (in vec2 uv) {\n        uv = min(max(uv, 0.0), 1.0);\n        return uv;\n    }\n\n    void main () {\n        float L = texture2D(uPressure, boundary(vL)).x;\n        float R = texture2D(uPressure, boundary(vR)).x;\n        float T = texture2D(uPressure, boundary(vT)).x;\n        float B = texture2D(uPressure, boundary(vB)).x;\n        float C = texture2D(uPressure, vUv).x;\n        float divergence = texture2D(uDivergence, vUv).x;\n        float pressure = (L + R + B + T - divergence) * 0.25;\n        gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);\n    }\n`);\n\nconst gradientSubtractShader = compileShader(gl.FRAGMENT_SHADER, `\n    precision highp float;\n    precision mediump sampler2D;\n\n    varying vec2 vUv;\n    varying vec2 vL;\n    varying vec2 vR;\n    varying vec2 vT;\n    varying vec2 vB;\n    uniform sampler2D uPressure;\n    uniform sampler2D uVelocity;\n\n    vec2 boundary (in vec2 uv) {\n        uv = min(max(uv, 0.0), 1.0);\n        return uv;\n    }\n\n    void main () {\n        float L = texture2D(uPressure, boundary(vL)).x;\n        float R = texture2D(uPressure, boundary(vR)).x;\n        float T = texture2D(uPressure, boundary(vT)).x;\n        float B = texture2D(uPressure, boundary(vB)).x;\n        vec2 velocity = texture2D(uVelocity, vUv).xy;\n        velocity.xy -= vec2(R - L, T - B);\n        gl_FragColor = vec4(velocity, 0.0, 1.0);\n    }\n`);\n\nlet textureWidth;\nlet textureHeight;\nlet density;\nlet velocity;\nlet divergence;\nlet curl;\nlet pressure;\ninitFramebuffers();\n\nconst clearProgram = new GLProgram(baseVertexShader, clearShader);\nconst displayProgram = new GLProgram(baseVertexShader, displayShader);\nconst splatProgram = new GLProgram(baseVertexShader, splatShader);\nconst advectionProgram = new GLProgram(baseVertexShader, ext.supportLinearFiltering ? advectionShader : advectionManualFilteringShader);\nconst divergenceProgram = new GLProgram(baseVertexShader, divergenceShader);\nconst curlProgram = new GLProgram(baseVertexShader, curlShader);\nconst vorticityProgram = new GLProgram(baseVertexShader, vorticityShader);\nconst pressureProgram = new GLProgram(baseVertexShader, pressureShader);\nconst gradienSubtractProgram = new GLProgram(baseVertexShader, gradientSubtractShader);\n\nfunction initFramebuffers() {\n    textureWidth = gl.drawingBufferWidth \u003e\u003e config.TEXTURE_DOWNSAMPLE;\n    textureHeight = gl.drawingBufferHeight \u003e\u003e config.TEXTURE_DOWNSAMPLE;\n\n    const texType = ext.halfFloatTexType;\n    const rgba = ext.formatRGBA;\n    const rg = ext.formatRG;\n    const r = ext.formatR;\n\n    density = createDoubleFBO(2, textureWidth, textureHeight, rgba.internalFormat, rgba.format, texType, ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST);\n    velocity = createDoubleFBO(0, textureWidth, textureHeight, rg.internalFormat, rg.format, texType, ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST);\n    divergence = createFBO(4, textureWidth, textureHeight, r.internalFormat, r.format, texType, gl.NEAREST);\n    curl = createFBO(5, textureWidth, textureHeight, r.internalFormat, r.format, texType, gl.NEAREST);\n    pressure = createDoubleFBO(6, textureWidth, textureHeight, r.internalFormat, r.format, texType, gl.NEAREST);\n}\n\nfunction createFBO(texId, w, h, internalFormat, format, type, param) {\n    gl.activeTexture(gl.TEXTURE0 + texId);\n    let texture = gl.createTexture();\n    gl.bindTexture(gl.TEXTURE_2D, texture);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n    gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, w, h, 0, format, type, null);\n\n    let fbo = gl.createFramebuffer();\n    gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);\n    gl.viewport(0, 0, w, h);\n    gl.clear(gl.COLOR_BUFFER_BIT);\n\n    return [texture, fbo, texId];\n}\n\nfunction createDoubleFBO(texId, w, h, internalFormat, format, type, param) {\n    let fbo1 = createFBO(texId, w, h, internalFormat, format, type, param);\n    let fbo2 = createFBO(texId + 1, w, h, internalFormat, format, type, param);\n\n    return {\n        get read() {\n            return fbo1;\n        },\n        get write() {\n            return fbo2;\n        },\n        swap() {\n            let temp = fbo1;\n            fbo1 = fbo2;\n            fbo2 = temp;\n        }\n    }\n}\n\nconst blit = (() =\u003e {\n    gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());\n    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]), gl.STATIC_DRAW);\n    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gl.createBuffer());\n    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 0, 2, 3]), gl.STATIC_DRAW);\n    gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\n    gl.enableVertexAttribArray(0);\n\n    return (destination) =\u003e {\n        gl.bindFramebuffer(gl.FRAMEBUFFER, destination);\n        gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);\n    }\n})();\n\nlet lastTime = Date.now();\nmultipleSplats(parseInt(Math.random() * 20) + 5);\nupdate();\n\nfunction update() {\n    resizeCanvas();\n\n    const dt = Math.min((Date.now() - lastTime) / 1000, 0.016);\n    lastTime = Date.now();\n\n    gl.viewport(0, 0, textureWidth, textureHeight);\n\n    if (splatStack.length \u003e 0)\n        multipleSplats(splatStack.pop());\n\n    advectionProgram.bind();\n    gl.uniform2f(advectionProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read[2]);\n    gl.uniform1i(advectionProgram.uniforms.uSource, velocity.read[2]);\n    gl.uniform1f(advectionProgram.uniforms.dt, dt);\n    gl.uniform1f(advectionProgram.uniforms.dissipation, config.VELOCITY_DISSIPATION);\n    blit(velocity.write[1]);\n    velocity.swap();\n\n    gl.uniform1i(advectionProgram.uniforms.uVelocity, velocity.read[2]);\n    gl.uniform1i(advectionProgram.uniforms.uSource, density.read[2]);\n    gl.uniform1f(advectionProgram.uniforms.dissipation, config.DENSITY_DISSIPATION);\n    blit(density.write[1]);\n    density.swap();\n\n    for (let i = 0; i \u003c pointers.length; i++) {\n        const pointer = pointers[i];\n        if (pointer.moved) {\n            splat(pointer.x, pointer.y, pointer.dx, pointer.dy, pointer.color);\n            pointer.moved = false;\n        }\n    }\n\n    curlProgram.bind();\n    gl.uniform2f(curlProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(curlProgram.uniforms.uVelocity, velocity.read[2]);\n    blit(curl[1]);\n\n    vorticityProgram.bind();\n    gl.uniform2f(vorticityProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(vorticityProgram.uniforms.uVelocity, velocity.read[2]);\n    gl.uniform1i(vorticityProgram.uniforms.uCurl, curl[2]);\n    gl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);\n    gl.uniform1f(vorticityProgram.uniforms.dt, dt);\n    blit(velocity.write[1]);\n    velocity.swap();\n\n    divergenceProgram.bind();\n    gl.uniform2f(divergenceProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(divergenceProgram.uniforms.uVelocity, velocity.read[2]);\n    blit(divergence[1]);\n\n    clearProgram.bind();\n    let pressureTexId = pressure.read[2];\n    gl.activeTexture(gl.TEXTURE0 + pressureTexId);\n    gl.bindTexture(gl.TEXTURE_2D, pressure.read[0]);\n    gl.uniform1i(clearProgram.uniforms.uTexture, pressureTexId);\n    gl.uniform1f(clearProgram.uniforms.value, config.PRESSURE_DISSIPATION);\n    blit(pressure.write[1]);\n    pressure.swap();\n\n    pressureProgram.bind();\n    gl.uniform2f(pressureProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(pressureProgram.uniforms.uDivergence, divergence[2]);\n    pressureTexId = pressure.read[2];\n    gl.uniform1i(pressureProgram.uniforms.uPressure, pressureTexId);\n    gl.activeTexture(gl.TEXTURE0 + pressureTexId);\n    for (let i = 0; i \u003c config.PRESSURE_ITERATIONS; i++) {\n        gl.bindTexture(gl.TEXTURE_2D, pressure.read[0]);\n        blit(pressure.write[1]);\n        pressure.swap();\n    }\n\n    gradienSubtractProgram.bind();\n    gl.uniform2f(gradienSubtractProgram.uniforms.texelSize, 1.0 / textureWidth, 1.0 / textureHeight);\n    gl.uniform1i(gradienSubtractProgram.uniforms.uPressure, pressure.read[2]);\n    gl.uniform1i(gradienSubtractProgram.uniforms.uVelocity, velocity.read[2]);\n    blit(velocity.write[1]);\n    velocity.swap();\n\n    gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n    displayProgram.bind();\n    gl.uniform1i(displayProgram.uniforms.uTexture, density.read[2]);\n    blit(null);\n\n    requestAnimationFrame(update);\n}\n\nfunction splat(x, y, dx, dy, color) {\n    splatProgram.bind();\n    gl.uniform1i(splatProgram.uniforms.uTarget, velocity.read[2]);\n    gl.uniform1f(splatProgram.uniforms.aspectRatio, canvas.width / canvas.height);\n    gl.uniform2f(splatProgram.uniforms.point, x / canvas.width, 1.0 - y / canvas.height);\n    gl.uniform3f(splatProgram.uniforms.color, dx, -dy, 1.0);\n    gl.uniform1f(splatProgram.uniforms.radius, config.SPLAT_RADIUS);\n    blit(velocity.write[1]);\n    velocity.swap();\n\n    gl.uniform1i(splatProgram.uniforms.uTarget, density.read[2]);\n    gl.uniform3f(splatProgram.uniforms.color, color[0] * 0.3, color[1] * 0.3, color[2] * 0.3);\n    blit(density.write[1]);\n    density.swap();\n}\n\nfunction multipleSplats(amount) {\n    for (let i = 0; i \u003c amount; i++) {\n        const color = [Math.random() * 10, Math.random() * 10, Math.random() * 10];\n        const x = canvas.width * Math.random();\n        const y = canvas.height * Math.random();\n        const dx = 1000 * (Math.random() - 0.5);\n        const dy = 1000 * (Math.random() - 0.5);\n        splat(x, y, dx, dy, color);\n    }\n}\n\nfunction resizeCanvas() {\n    if (canvas.width != canvas.clientWidth || canvas.height != canvas.clientHeight) {\n        canvas.width = canvas.clientWidth;\n        canvas.height = canvas.clientHeight;\n        initFramebuffers();\n    }\n}\n\ncanvas.addEventListener('mousemove', (e) =\u003e {\n    pointers[0].moved = pointers[0].down;\n    pointers[0].dx = (e.offsetX - pointers[0].x) * 10.0;\n    pointers[0].dy = (e.offsetY - pointers[0].y) * 10.0;\n    pointers[0].x = e.offsetX;\n    pointers[0].y = e.offsetY;\n});\n\ncanvas.addEventListener('touchmove', (e) =\u003e {\n    e.preventDefault();\n    const touches = e.targetTouches;\n    for (let i = 0; i \u003c touches.length; i++) {\n        let pointer = pointers[i];\n        pointer.moved = pointer.down;\n        pointer.dx = (touches[i].pageX - pointer.x) * 10.0;\n        pointer.dy = (touches[i].pageY - pointer.y) * 10.0;\n        pointer.x = touches[i].pageX;\n        pointer.y = touches[i].pageY;\n    }\n}, false);\n\ncanvas.addEventListener('mousedown', () =\u003e {\n    pointers[0].down = true;\n    pointers[0].color = [Math.random() + 0.2, Math.random() + 0.2, Math.random() + 0.2];\n});\n\ncanvas.addEventListener('touchstart', (e) =\u003e {\n    e.preventDefault();\n    const touches = e.targetTouches;\n    for (let i = 0; i \u003c touches.length; i++) {\n        if (i \u003e= pointers.length)\n            pointers.push(new pointerPrototype());\n\n        pointers[i].id = touches[i].identifier;\n        pointers[i].down = true;\n        pointers[i].x = touches[i].pageX;\n        pointers[i].y = touches[i].pageY;\n        pointers[i].color = [Math.random() + 0.2, Math.random() + 0.2, Math.random() + 0.2];\n    }\n});\n\nwindow.addEventListener('mouseup', () =\u003e {\n    pointers[0].down = false;\n});\n\nwindow.addEventListener('touchend', (e) =\u003e {\n    const touches = e.changedTouches;\n    for (let i = 0; i \u003c touches.length; i++)\n        for (let j = 0; j \u003c pointers.length; j++)\n            if (touches[i].identifier == pointers[j].id)\n                pointers[j].down = false;\n});\n```\n\u003cscript\u003e\n@input\n\u003c/script\u003e\n\n\u003ccanvas id=\"glCanvas\" width=\"600\" height=\"400\"\u003e\u003c/canvas\u003e\n\n\n#### Familenschacht Freiberg\n\u003c!--\npersistent: true\n--\u003e\n\n    {{1}}\n\u003cdiv style=\"width:100%;height:0;padding-bottom:100%;position:relative;\"\u003e\u003ciframe src=\"https://giphy.com/embed/1uyFaGpt2ilmE\" width=\"100%\" height=\"100%\" style=\"position:absolute\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/grandma-1uyFaGpt2ilmE\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n\n    {{2}}\n??[3D Modell](https://sketchfab.com/3d-models/familienschacht-freiberg-germany-7c7d30506c554385a4a4321366e2e601 \"_Source: TU Bergakademie Freiberg, Saxonia-Freiberg-Stiftung_\")\n\n\n### Weiter Beispiele\n\n\u003cdiv style=\"width:100%;height:0;padding-bottom:100%;position:relative;\"\u003e\u003ciframe src=\"https://giphy.com/embed/BrkuIkfzokEWJ7tSM5\" width=\"100%\" height=\"100%\" style=\"position:absolute\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/fomoduck-duck-highstreet-fomo-BrkuIkfzokEWJ7tSM5\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n\n#### Text to Speech\n\n{{1 |\u003e}}\nHallo und Willkommen beim Robotiklabor in Freiberg.\n\n{{2 |\u003e French Male}}\nBonjour et bienvenue au Laboratoire de Robotique à Freiberg.\n\n{{3 |\u003e Chinese Male}}\n你好，欢迎来到弗莱贝格的机器人实验室。\n\n#### Linux Emulator\n\u003c!--\npersistent: true\n--\u003e\n\n\u003ciframe src=\"https://bellard.org/jslinux/vm.html?url=alpine-x86.cfg\u0026mem=192\" style=\"height: 630px; width: 100%\"\u003e\u003c/iframe\u003e\n\n\n#### DOS Archive\n\u003c!--\npersistent: true\n--\u003e\n\nhttps://archive.org/details/softwarelibrary_msdos\n\n\u003ciframe src=\"https://archive.org/details/doom-play#\" style=\"height: 630px; width: 100%\"\u003e\u003c/iframe\u003e\n\n\n## Neue Technologien\n\n\u003cdiv style=\"width:100%;height:0;padding-bottom:50%;position:relative;\"\u003e\u003ciframe src=\"https://giphy.com/embed/3o72FhGxehNdI4Ukc8\" width=\"100%\" height=\"100%\" style=\"position:absolute\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/audi-3o72FhGxehNdI4Ukc8\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n\n### PWA\n\n\u003e Eine __P__rogressive __W__eb __A__pplication (PWA) oder Progressive Web-App ist eine Art von Anwendungssoftware, die über das Web bereitgestellt wird und mit gängigen Webtechnologien wie HTML, CSS, JavaScript und WebAssembly entwickelt wird. Sie ist dafür gedacht, auf jeder Plattform mit einem standardkonformen Browser zu funktionieren, einschließlich Desktop- und Mobilgeräten...\n\u003e\n\u003e _Quelle: [Wikipedia](https://de.wikipedia.org/wiki/Progressive_Web-App)_\n\n    {{1}}\n\u003csection\u003e\n__Vorteile:__\n\n* Erlaubt das Speichern der Website im Browser\n* Kann wie eine native App installiert werden\n* Unterstützung für fortschrittliche Caching-Strategien\n\u003c/section\u003e\n\n    {{2}}\n\u003cdiv style=\"width:100%;height:0;padding-bottom:67%;position:relative;\"\u003e\u003ciframe src=\"https://giphy.com/embed/xUA7b6waIz1Jdfp0Qw\" width=\"100%\" height=\"100%\" style=\"position:absolute\" frameBorder=\"0\" class=\"giphy-embed\" allowFullScreen\u003e\u003c/iframe\u003e\u003c/div\u003e\u003cp\u003e\u003ca href=\"https://giphy.com/gifs/reactionseditor-xUA7b6waIz1Jdfp0Qw\"\u003evia GIPHY\u003c/a\u003e\u003c/p\u003e\n\n\n### IndexedDB\n\n\u003e Die __Indexed Database API__, kurz IndexedDB, ist eine Programmierschnittstelle, die es Webseiten mittels JavaScript erlaubt, strukturierte Daten im Browser zu speichern. Der Standard wird vom World Wide Web Consortium entwickelt und ist in Browsern ab 2011 implementiert.\n\u003e\n\u003e Quelle: https://de.wikipedia.org/wiki/Indexed_Database_API\n\n### Peer^2^Peer in da Browser\n\n    {{1-2}}\n``` ascii    __Fig:__ Klassische Client \u0026 Server Application\n    👨🏾‍💻 --.     .-- 👩‍💻\n          \\   /\n\n👩‍💻 ------  🖥️  ------ 👨🏾‍💻\n\n          /   \\\n    👨🏾‍💻 --'     '-- 👩‍💻\n```\n\n    {{2}}\n``` ascii    __Fig:__ Peer^2^Peer- Netzwerk\n- - - --👨🏾‍💻-----👩‍💻\n              /  \\\n             /    \\\n    👩‍💻------+-----👨🏾‍💻- - -\n      \\    /      /\n       \\  /      /\n        👨🏾‍💻     👩‍💻- - - -\n```\n\n#### WebRTC\n\n\u003e __Web Real-Time Communication__ ist ein freies Open-Source-Projekt, das Webbrowsern und mobilen Anwendungen Echtzeitkommunikation (RTC) über Anwendungsprogrammierschnittstellen (APIs) bietet. Es ermöglicht Audio- und Videokommunikation innerhalb von Webseiten, indem es direkte Peer-to-Peer-Kommunikation ermöglicht und die Notwendigkeit von Plug-Ins oder das Herunterladen von nativen Apps beseitigt...\n\u003e\n\u003e _Quelle: [Wikipedia](https://en.wikipedia.org/wiki/WebRTC)_\n\n\n\n    {{0-1}}\n``` ascii\n                     (WebRTC)\nAlice 👩‍💻 \u003c------------------------------\u003e 👨🏾‍💻 Bob\n```\n\n    {{1}}\n``` ascii\n               (Signaling Server)\n\n       .-------------\u003e 🖥️ --------------.\n       |    \"{1}{}\"   /  A      \"{2}{}\" |\n       |             /    \\             |\n       |            /      \\            V\n                   /        \\\nAlice 👩‍💻 \u003c--------'          '--------- 👨🏾‍💻 Bob\n            \"{4}{}\"             \"{3}{}\"\n      A                                  A\n      |                                  |\n      '----------------------------------'\n      A    \"{5}{Direct Communication}\"   A\n      |                                  |\n      '-- - - - - - - - - - - - - - - - -'\n             \"{6}{InDirect via TURN}\"\n```\n\n    {{7}}\n\u003e Eine noch konfusere Erklärung zu WebRTC:\n\u003e\n\u003e !?[WebRTC](https://www.youtube.com/watch?v=7cbD-hFkzY0\u0026start=410)\n\n    {{8}}\nPeer2Peer teilen von Dateien über den Browser: https://instant.io/\n\n### CRDTs\n\nEin _**C**onfliktfreier **R**eplizierter **D**atentyp_ (CRDT) sind neue Datentypen[^1], die über mehrere Instanzen in einem Netzwerk repliziert werden können und folgende Garantien bieten:\n\n    {{1}}\n1. Eine Replik kann unabhängig, gleichzeitig und ohne Koordination mit anderen Repliken aktualisiert werden.\n2. Inkonsistenzen können automatisch aufgelöst werden.\n3. Obwohl Repliken unterschiedliche Zustände haben können, ist sichergestellt, dass sie letztendlich konvergieren.\n\n    {{2}}\n__Aufgabe:__ Implementiere einen verteilten Zähler\n\n    {{3}}\n``` ascii\nAlice 👩‍💻\n\n[0]---------*--\u003e[5]--[+1 = 6]--------*--\u003e[8]-- - - - - - - - - - - - \n           /            \\           /          \\\n          A              V         A            \\\n         /                        /              \\\n[0]---[+5 = 5]-----------------[+2 = 7]-- - - - --*- - - - - - - - - -\n\nBob 👨🏾‍💻\n```\n\n    {{4}}\n__Solution:__ Use Sets and Unions instead... \n\n    {{5}}\n``` ascii\nAlice 👩‍💻\n\n{(a,0)}----------*--\u003e{(a,0),(b,5)}-\u003e{(a,1),(b,5)}---*--\u003e{(a,1),(b,7)}\n                /                         \\        /   \n               A                           V      A   \n              /                                  /\n{(b,0)}---{(b,5)}----------------------------{(b,7)}-----------------\n\nBob 👨🏾‍💻\n```\n\n    {{6}}\n\u003csection\u003e\n\n__ Implementations__\n\n- [Automerge](https://automerge.org)\n- [__Yjs__](https://docs.yjs.dev)\n\n\u003c/section\u003e\n\n\n[^1]: The CRDT concept was defined in 2011 by Marc Shapiro, Nuno Preguiça, Carlos Baquero and Marek Zawirski.\n\n      See also: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type\n\n\n## Bildung - OER\n\n![OER-Logo](https://upload.wikimedia.org/wikipedia/commons/2/20/Global_Open_Educational_Resources_Logo.svg)\n\n### LiaScript\n\nErweiterung von Markdown zum Erstellen von interaktiven OER-Inhalten\n\n* https://LiaScript.github.io\n\n* https://LiaScript.github.io/LiveEditor\n\nVersuchen sie es selbst:\n\nhttps://liascript.github.io/LiveEditor/?/show/file/https://raw.githubusercontent.com/LiaPlayground/RoboLab2024/main/README.md\n\n#### Technische Informatik\n\n``` json @DigiSim.evalJson\n{\n  \"devices\": {\n    \"dev0\": {\n      \"label\": \"s\",\n      \"position\": {\n        \"x\": 0,\n        \"y\": 20\n      },\n      \"celltype\": \"$button\",\n      \"propagation\": 0\n    },\n    \"dev1\": {\n      \"label\": \"r\",\n      \"position\": {\n        \"x\": 155,\n        \"y\": 75\n      },\n      \"celltype\": \"$button\",\n      \"propagation\": 0\n    },\n    \"dev2\": {\n      \"label\": \"q\",\n      \"position\": {\n        \"x\": 480,\n        \"y\": 55\n      },\n      \"celltype\": \"$lamp\",\n      \"propagation\": 1\n    },\n    \"dev3\": {\n      \"label\": \"nq\",\n      \"position\": {\n        \"x\": 325,\n        \"y\": 0\n      },\n      \"celltype\": \"$lamp\",\n      \"propagation\": 1\n    },\n    \"dev6\": {\n      \"label\": \"$or$_input.sv:7$1\",\n      \"position\": {\n        \"x\": 310,\n        \"y\": 50\n      },\n      \"celltype\": \"$nor\",\n      \"propagation\": 1,\n      \"bits\": 1\n    },\n    \"dev7\": {\n      \"label\": \"$or$_input.sv:8$3\",\n      \"position\": {\n        \"x\": 140,\n        \"y\": 15\n      },\n      \"celltype\": \"$nor\",\n      \"propagation\": 1,\n      \"bits\": 1\n    }\n  },\n  \"connectors\": [\n    {\n      \"from\": {\n        \"id\": \"dev0\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev7\",\n        \"port\": \"in1\"\n      },\n      \"name\": \"s\",\n      \"vertices\": []\n    },\n    {\n      \"from\": {\n        \"id\": \"dev1\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev6\",\n        \"port\": \"in1\"\n      },\n      \"name\": \"r\",\n      \"vertices\": []\n    },\n    {\n      \"from\": {\n        \"id\": \"dev6\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev2\",\n        \"port\": \"in\"\n      },\n      \"name\": \"q\",\n      \"vertices\": []\n    },\n    {\n      \"from\": {\n        \"id\": \"dev6\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev7\",\n        \"port\": \"in2\"\n      },\n      \"name\": \"q\",\n      \"vertices\": []\n    },\n    {\n      \"from\": {\n        \"id\": \"dev7\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev3\",\n        \"port\": \"in\"\n      },\n      \"name\": \"nq\",\n      \"vertices\": []\n    },\n    {\n      \"from\": {\n        \"id\": \"dev7\",\n        \"port\": \"out\"\n      },\n      \"to\": {\n        \"id\": \"dev6\",\n        \"port\": \"in2\"\n      },\n      \"name\": \"nq\",\n      \"vertices\": []\n    }\n  ],\n  \"subcircuits\": {}\n}\n```\n\n#### Musik\n\n``` abc\nX:353\nT: GLUECK AUF DER STEIGER KOEMMT\nN: E1512\nO: Europa, Mitteleuropa, Deutschland\nR: Staende -, Bergmanns - Lied\nM: 4/4\nL: 1/16\nK: G\n | G8F4A4 | G8z8 |\nB8A4c4 | B8z4\nG2A2 | B4B4B4A2B2 | c4A3AA4\nA2B2 | c4c4c4B2c2 | d4B3BB4\nA4 | G8F8 | G4e4d4\nc2A2 | B8A8 | G8z8\n```\n@ABCJS.eval\n\n#### Medizin\n\n@VTK.loadIframe(https://kitware.github.io/vtk-js-datasets/data/vti/head-binary-zlib.vti)\n\n### CrossLab\n\nhttps://edrys-labs.github.io/edrys-Lite/","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliaplayground%2Frobolab2024","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliaplayground%2Frobolab2024","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliaplayground%2Frobolab2024/lists"}