Skip to content
hardikverma22Public

About

A fast, free playground for HTML, CSS and JavaScript. Write code, watch it run, and share a short link anyone can open and fork. No account

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

CodeMe

A fast, free playground for HTML, CSS and JavaScript. Write code, watch it run, and share a short link anyone can open and fork. No account.

Live demo · Templates · How it works · Design decisions

TypeScript React Vite Tailwind CSS Convex License: MIT

CodeMe landing page: a headline, a live stage of code being typed, and the result building beside it

Why CodeMe

Most playgrounds make you pick between instant and simple (a blank page that runs) and powerful but heavy (a cloud IDE with a build step). CodeMe aims at the first group and adds the parts people actually need when they share a snippet:

  • A link is the whole project. Press Share and you get a short link. Whoever opens it can read it, run it and change it.
  • Forking is built in. Open a link, edit, Share again: you get your own copy that remembers where it came from. Nothing is ever overwritten, so nobody can vandalise a link.
  • No account, no setup. Your work autosaves in the browser until you share it.
  • Honest about its limits. The sandbox, abuse model and Tailwind caveats are written down in Design decisions.

Features

Editor

  • Three CodeMirror 6 editors (HTML, CSS, JavaScript) with Emmet, syntax colours matched to each language, and one-click Prettier formatting
  • Live preview in a sandboxed iframe with a built-in console
  • Console errors point at your JavaScript line (ReferenceError … (JS line 4)), not the generated document
  • Accessibility tab next to the console: missing alt text, unlabelled fields, unnamed buttons and links, skipped headings, duplicate ids and colour contrast, each with its HTML line where it can be found
  • Fix button on every console error and accessibility finding. Rules fix the mechanical ones instantly (labels, heading levels, tabindex, duplicate ids, colour contrast in the CSS rule that sets it). For the rest, Chrome's built-in on-device model (Gemini Nano) can propose a fix. Either way the change is run in a hidden sandbox first and shown only if the problem is gone and nothing new appeared; you review a diff and choose Apply. See Verified fixes
  • Compare to image: lay a screenshot or mockup over the preview (fade, split or difference). The image stays in your browser and is not saved or shared
  • Resizable panels, a mobile layout, and reduced-motion support

Libraries

  • Tailwind CSS v4 toggle (browser build), including @apply in the CSS tab
  • With Tailwind on: class-name suggestions inside class="…" (variants too), and To Tailwind, which previews your plain CSS rewritten as @apply before replacing it. Declarations with no utility, media queries and keyframes are left as they are
  • External scripts and styles: add up to five https links, loaded in order before your code (Chart.js, GSAP, Lucide, and so on)

Sharing

  • Short links at /p/<slug>, stored as immutable snapshots
  • Identical code always returns the same link
  • Fork lineage shown in the editor header, and a Changes view with a line diff against the link you opened or the project it was forked from
  • Export any project as a small static site (ZIP)

Starting points

  • Nine ready-made templates, including a multi-library Mission Control dashboard
  • A local "My work" list of everything you have shared

The editor running the Mission Control dashboard template: three code panes, a live preview with charts, and a console

A template that uses everything

Mission Control is an analytics dashboard built from Tailwind v4, Chart.js, GSAP and Lucide, all loaded through the playground's own Settings panel. It exists to show that a multi-library page runs with zero setup: open the template, and the framework toggle and external resources are configured for you.

Quick start

Requires Node 20.19+ (or 22.12+) and npm.

git clone https://github.com/hardikverma22/code-me.git
cd code-me
npm install
npm run dev          # http://localhost:5173

The editor, preview, templates and URL-hash sharing work immediately. Short links and forks need the backend:

npx convex dev       # log in, create a project; writes VITE_CONVEX_URL to .env.local

Scripts

Command What it does
npm run dev Start the Vite dev server
npm run build Type-check, then build for production
npm test Run the unit tests (Vitest)
npm run lint Lint with ESLint

Environment variables

Variable Where Required Purpose
VITE_CONVEX_URL Vercel / .env.local For short links Convex deployment URL. The share endpoint is derived from it. Without it, Share falls back to a compressed URL hash.
VITE_TURNSTILE_SITE_KEY Vercel / .env.local Optional Public Cloudflare Turnstile site key. Turns on the bot challenge in the browser.
TURNSTILE_SECRET Convex dashboard Optional Turnstile secret key. When set, the server requires a valid challenge token to create a share.
ALLOWED_ORIGIN Convex dashboard Optional Restricts browser access to the share endpoint to your site's origin.

Architecture

flowchart LR
  B[Browser<br/>React + CodeMirror] -->|runs your code| P[Sandboxed iframe<br/>allow-scripts only]
  P -->|console and errors<br/>via postMessage| B
  B -->|Share: POST /share<br/>+ Turnstile token| H[Convex HTTP action<br/>validate · challenge · rate limit]
  H --> M[Internal mutation<br/>dedupe by hash · insert]
  M --> D[(projects table)]
  B -->|open /p/slug| Q[Public query<br/>getBySlug]
  Q --> D
Loading
  • Frontend: Vite, React 19, TypeScript, Tailwind CSS 4, Zustand, GSAP and Lenis for the landing page.
  • Backend: Convex. The only way to create a share is the /share HTTP action in front of an internal mutation; clients cannot write to the database directly.
  • Hosting: Vercel for the static app, Convex for the data.
src/
  pages/        Landing (+ sections), Editor, Gallery, My work
  components/   Editor, panels, preview, header, settings
  fx/           Motion: smooth scroll, cursor, flow field, kinetic text
  lib/          srcDoc builder, share API, templates, export, tests
  store/        Zustand stores (project, console, saved work)
convex/
  http.ts       POST /share: validation, challenge, rate limits
  projects.ts   Internal create mutation, public getBySlug query
  limits.ts     Rate-limit definitions

Design decisions

The preview sandbox has no allow-same-origin. The iframe uses sandbox="allow-scripts allow-modals allow-forms". Combining allow-scripts with allow-same-origin lets the embedded page remove its own sandbox, so it is never added (MDN, OWASP). A unit test fails if it ever is. Popups and top-level navigation are also off, so window.open inside a preview does nothing by design.

Sharing is a snapshot, not a live document. A share can never be edited by anyone, including its author, so a link cannot be vandalised. Forking is "open, edit, Share again": it creates a new snapshot that records its parent. The tradeoff is that an author cannot update a link in place.

Convex only stores short links and forks. Running code never needs a server. Without a backend configured, Share falls back to a compressed URL hash (lz-string), so the app still works.

Errors point at your line. The preview injects the line offset of your JavaScript, so an error refers to line 4 of your script, not line 31 of the generated page. A test pins this behaviour.

Tailwind runs through its browser build. Tailwind documents this build as development-only. CodeMe uses it so snippets work with no setup, says so in the Settings panel, and the Export download adds a note to use the Tailwind CLI for production.

Security and abuse model

Anyone can call a public endpoint, so the write path is metered rather than trusted.

Layer What it does
Private mutation create is internal. The only entry point is POST /share.
Input validation Types, size (about 200 KB of code, 400 KB per request), https-only resource links, at most five.
Duplicate check Content is hashed with SHA-256. Identical code returns the existing link and uses no quota.
Rate limits Per IP: 10 new shares a day and a burst of 3. Globally: 1,500 shares an hour as a circuit breaker. Built on @convex-dev/rate-limiter, in the same transaction as the insert.
Bot challenge Optional Cloudflare Turnstile, verified server-side. Tokens are single-use.
No enumeration Slugs are eight characters from a 32-letter alphabet (about 10^12 combinations) and nothing lists projects.

Not covered: a large distributed attack from many IPs is bounded by the global cap and Turnstile, not eliminated. Old snapshots are not deleted yet, so a retention job is a sensible next step.

Verified fixes without a server

The sandbox is the judge, not the model. A fix from any source (a rule, or Gemini Nano) is applied to a copy of the page and run in a hidden frame with the same sandbox flags as the preview. It is offered only if the problem is gone, no new finding or error appeared, and the page still finishes loading. A guard also turns down fixes that delete elements or text, rewrite most of a tab, or change nothing, since deleting the broken element would otherwise "fix" it. When a try fails, the reason goes back to the model for the next one (up to three).

Nothing is paid for and nothing leaves the device. The model is Chrome's Prompt API: Chrome downloads and runs it locally, so there is no key, no account and no server cost. It needs desktop Chrome with enough disk space and memory, so elsewhere you still get the rule-based fixes. The first model use asks before Chrome downloads anything, and the dialog says your code stays on the device. The page, not the sandboxed preview, calls the model, so code you run cannot reach it.

The model sees one tab, and answers with one tab. Sending a whole page would make anything bigger than a toy too long for the model's context, so each problem gets only the code that can fix it: a runtime error gets the JavaScript plus the list of element ids, contrast gets the CSS, and the other accessibility problems get the HTML.

The model answers with one tab, not all three. Early tests asked for all three tabs back, and a small model reformatted code it was never asked to touch. Asking for exact find-and-replace text failed on copying. Returning the full new code of the single tab it changes, plus a short hint for common runtime errors, gave the best results: on a page with several problems, 23 of 24 attempts passed (17 on the first try), measured with the app's own fix loop in Chrome 155. That is one machine, one model version and a handful of cases, not a general claim.

Testing

Unit tests cover the sandbox flags, error-line mapping, Tailwind injection, the share-link codec, template validity, the diff, the Tailwind converter, the accessibility checker, and the fix loop (rules checked against the real checker, the verifier, the guard, retries with feedback, and the model engine against a fake LanguageModel).

npm test

The abuse limits were also checked against a deployed endpoint: bad input, oversized bodies, bursts, duplicate shares and token replay.

Roadmap

  • Retention policy for old snapshots
  • Production Convex deployment with a locked-down origin
  • Real-time collaboration in a single room (Yjs and y-codemirror.next)
  • Social preview images for shared links
  • Fixes for more accessibility rules (unnamed buttons and links still need the model)
  • Accounts, only if a feature needs them

Known limits

  • Tailwind and external libraries load from a CDN, so they need an internet connection.
  • No npm packages or bundler: the preview is a plain browser document by design.
  • "My work" is stored on the device until accounts exist.

License

MIT © 2026 hardikverma22

Acknowledgements

Built with CodeMirror, Convex, GSAP, Prettier, Emmet, Tailwind CSS and Lucide.

About

A fast, free playground for HTML, CSS and JavaScript. Write code, watch it run, and share a short link anyone can open and fork. No account

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages