What I Learned Building A Code Playground
Hatem Hosny
Agenda
- Project
- Challenges
- Design decisions and work-arounds
- Web Standards and APIs
- Conclusions
Disclosures
I'm a doctor!
... and the author of LiveCodes
Language Support
Features
Embedded Playgrounds
- Can be embedded in any web page
- Powerful SDK
- JavaScript/TypeScript
- React
- Vue
- Svelte
- Communicate with embedded playgrounds
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
Definitions
- Language
- Compiler
- Runtime
Running 80+ Languages
- Options:
- Servers
- WebContainers/Nodebox
- Use the platform!
- JavaScript
- WebAssembly (Wasm)
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
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"
Getting Imports
https://regexr.com/
Unit tests
No Backend!
- Save
- Deploy
- Local assets
- Sync
- Share
- Broadcast
Browser Storage
- Cookies
- sessionStorage
- localStorage
- IndexedDB
- Web SQL ❌
- Cache API
- Origin private file system (OPFS)
Services
- CDNs
- npm packages
- ES modules for npm packages
- GitHub files
- Deno modules
- GitHub Pages
- Share service: dpaste
- Custom services
Query Strings
https://livecodes.io/?x=code/N4IgLglmA2CmIC...
- Client-side sharing
- Size limit
- Needs encoding and may be compression
- Not user-friendly
Data URLs
URLs prefixed with the data:
scheme
Example:
data:,Hello%2C%20World%21
↓
Hello, World!
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
release --> App
App -- GH Actions --> PR[PR -> main]
PR -- CF Pages --> Preview
Preview -- DNS API --> Subdomain[v15.livecodes.io]
PR -- merge --> Deploy
Deploy --> Domain[livecodes.io]
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!
- Source control
- Collaboration
Summary
- WebAssembly
- Higher-Order Functions
- Import Maps
- Browser Storage
- Data URLs
- Automated Releases - Versions
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/HTTP/Basics_of_HTTP/Data_URLs