What I Learned Building A Code Playground

Hatem Hosny

Agenda

  • Project
  • Challenges
    • Design decisions and work-arounds
    • Web Standards and APIs
    • Performance
    • Security
  • Conclusions

Disclosures

I'm a doctor!

... and the author of LiveCodes

LiveCodes

A Code Playground That Just Works!

https://livecodes.io

Language Support

Features

Embedded Playgrounds

  • Can be embedded in any web page
  • Powerful SDK
    • JavaScript/TypeScript
    • React
    • Vue
    • Svelte
  • Communicate with embedded playgrounds

Demo

Client-Side!

  • Yes, all compilers run in the browser!
  • No server rounds
  • No servers to configure (or pay for!)
  • All projects are private by default
  • Free with unlimited usage
  • Can be self-hosted on any static file server

Free and Open-Source

  • MIT License
  • No limits for use
  • No ads
  • No accounts required

 

https://github.com/live-codes/livecodes

Challenges

Running 80+ Languages

Running 80+ Languages

  • Options:
    • Servers
    • WebContainers/Nodebox
    • Use the platform!
      • JavaScript
      • WebAssembly (Wasm)
node_modules

Languages

            graph LR
                A[/Language A\n`compiles to JS`/]
                B{{Language B\n`JS runtime`}}
                C>Language C\n`Wasm runtime`]
          

Create Compilers

            graph LR
                comp[Create Compiler] -- Config --> A[/Language A\n`compiles to JS`/]
                comp -- Config --> B{{Language B\n`JS runtime`}}
                comp -- Config --> C>Language C\n`Wasm runtime`]
                A -- "⌛" --> fnA(fn A)
                fnA -- code --> Script
                B -- "⌛" --> fnB(fn B)
                fnB -- code --> Script
                C -- "⌛" --> fnC(fn C)
                fnC -- code --> Script
          

Demo: Ruby

Demo: PHP

Importing npm Modules

Importing npm Modules

Importing npm Modules

import {v4} from "uuid"

Uncaught TypeError: Failed to resolve module specifier "uuid". Relative references must start with either "/", "./", or "../".

Importing npm Modules

import {v4} from "https://jspm.dev/uuid"

No Backend!

No Backend!

  • Save
  • Deploy
  • Local assets
  • Sync
  • Share
  • Broadcast

Browser Storage

  • Cookies
  • sessionStorage
  • localStorage
  • IndexedDB
  • Web SQL
  • Cache API

Services

  • CDNs
    • npm packages
    • ES modules for npm packages
    • GitHub files
    • Deno modules
  • GitHub Pages
    • Push to gh-pages branch
  • Share service: dpaste
  • Custom services

Data URLs

URLs prefixed with the data: scheme

Example:

data:,Hello%2C%20World%21

Hello, World!

Avoid Breaking Changes

SDK

  • for embedding playgrounds
  • light-weight npm package (SDK)
  • creates an Iframe
  • loads LiveCodes with embed options (App)
  • allows communication with the app

Automated Releases

            graph LR
                release[Release] --> SDK
                SDK -- GH Actions --> npm
                npm --> sdkV["livecodes@0.2.0"]
                sdkV --> CDNs
                SDK -- commits --> notes[CHANGE LOG]
                release --> App
                App -- commits --> notes
                notes --> ghRelease[GitHub Release]
                App -- GH Actions --> PR[PR -> main]
                PR -- CF Pages --> Preview
                Preview -- DNS API --> Subdomain[v14.livecodes.io]
                PR -- merge --> Deploy
                Deploy --> Domain[livecodes.io]
          

Compiler Performance

Compiler Performance

  • Caching
  • Web workers

Caching

Web Workers

Web Workers

  • self instead of window
  • No access to DOM
  • Message data must be serializable
    (No functions or methods)
  • Worker script must be from same origin
  • But, you can importScripts() from anywhere!
    // worker.js
    importScripts("https://unpkg.com/lib/script.js");
  • Data URLs are allowed ;)

Security

Result Page

Sandboxed Iframes

Technology Stack

(a.k.a. Personal Preferences!)

  • "Vanilla" TypeScript
  • I NEVER use classes or this!
  • Build system: Esbuild
  • Testing: Playwright & Jest
  • Docs: Docusaurus
  • CI/CD: GitHub Actions
  • Hosting: Cloudflare Pages
  • Backend: None :)

Future

  • Offline!
    • Service workers
  • Source control
    • Client-side Git!
  • Collaboration
    • WebRTC

Summary

  • WebAssembly
  • Higher-Order Functions
  • Import Maps
  • Automated Releases - Versions
  • Caching
  • Web Workers
  • Data URLs
  • Sandboxed Iframes

Conclusions

Open-Source = ❤️

Keep learning and building

References

  • https://webassembly.org/
  • https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script/type/importmap
  • https://github.com/WICG/import-maps
  • https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Client-side_storage
  • https://web.dev/persistent-storage/
  • https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
  • https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
  • https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs
  • https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
  • https://web.dev/sandboxed-iframes/

 

https://livecodes.io/?x=id/qw7fp57scqy

Made in LiveCodes!

Thank You

Thank You