An infinite canvas where you can write and run JavaScript freely — inspired by Excalidraw. Drop code blocks anywhere on the canvas, execute them in real time, and share your canvas with others.
- Infinite canvas — drag, drop, and arrange code blocks anywhere
- Live code execution — run JavaScript and see output instantly
- Real-time updates — changes sync across clients via Socket.IO
- Job queue — code runs are queued through BullMQ so the server stays stable under load
- Canvas sharing — share a read-only link to your canvas with others
- Authentication — sign up, log in, and manage your own canvases
- Dark / light mode
| Layer | Technology |
|---|---|
| Frontend | React 19, TypeScript, Vite, Tailwind CSS, CodeMirror |
| Backend | Node.js, Express, TypeScript, Socket.IO |
| Database | PostgreSQL |
| Queue/Cache | Redis, BullMQ |
| Container | Docker, Docker Compose, nginx |
To run with Docker (recommended):
- Docker
- Docker Compose (included with Docker Desktop)
To run locally without Docker:
- Node.js 20+
- PostgreSQL
- Redis
-
Clone the repo:
git clone https://github.com/rcjasub/free-form-Code.git cd free-form-Code -
Create the backend env file:
cp backend/.env.example backend/.env
Fill in the values (see Environment Variables).
-
Start everything:
docker compose up --build
-
Open http://localhost in your browser.
To stop:
docker compose down-
Install dependencies:
cd backend && npm install cd ../my-app && npm install
-
Make sure PostgreSQL and Redis are running locally, then create
backend/.env(see below). -
Start both servers from the
my-appfolder:npm run dev
This starts the backend on
http://localhost:4000and the frontend onhttp://localhost:5173.
Create backend/.env with the following:
PORT=4000
# Database
DB_HOST=localhost
DB_PORT=5432
DB_NAME=freeformcode
DB_USER=postgres
DB_PASSWORD=yourpassword
# Redis
REDIS_URL=redis://localhost:6379
# Auth
JWT_SECRET=your_jwt_secret
# Frontend origin (for CORS)
CLIENT_URL=http://localhost:5173When running with Docker,
DB_HOSTandREDIS_URLare overridden automatically bydocker-compose.ymlto use the container service names.
free-form-Code/
├── backend/
│ ├── controllers/ # Route handlers (auth, canvas, blocks, run)
│ ├── routes/ # Express route definitions
│ ├── models/ # Database models
│ ├── middleware/ # Auth middleware
│ ├── queue.ts # BullMQ job queue setup
│ ├── worker.ts # Queue worker (processes code execution jobs)
│ ├── redis.ts # Redis client
│ ├── socket.ts # Socket.IO setup
│ ├── server.ts # Entry point
│ └── Dockerfile
├── my-app/
│ ├── src/
│ │ ├── pages/ # Dashboard, Canvas, Home, SharedCanvas
│ │ ├── components/ # UI components (CodeBlock, FloatingNode, etc.)
│ │ ├── API/ # Axios API calls
│ │ └── hooks/ # Custom React hooks
│ ├── nginx.conf # Serves SPA + proxies /api and /socket.io to backend
│ └── Dockerfile
├── docs/
│ ├── docker.md # Docker concepts and setup explained
│ └── redis-and-queues.md # Queue and caching architecture explained
└── docker-compose.yml # Orchestrates all four services
When a user clicks Run, the request is not executed immediately. Instead:
- Express adds the job to a BullMQ queue backed by Redis
- A separate worker process picks up the job and runs the code
- The result is sent back to the specific user via Socket.IO using their
socketId
This keeps the server stable under load — see docs/redis-and-queues.md for a full breakdown.
