Interactive infinite canvas for collaborative human expression. Built with React + Pixi.js + Node.js + PostgreSQL.
Live Demo: [Coming Soon]
An infinite zoomable canvas where users contribute text, drawings, images, or audio. Each contribution gets a tile positioned using a spiral algorithm. Think Reddit's r/place meets collaborative digital art.
Key Features:
- Infinite pan/zoom canvas (Pixi.js + pixi-viewport)
- 4 contribution types with unique IDs (HM-XXXXX)
- Rate limiting (15 req/15min per IP)
- Position collision detection (no overlapping tiles)
- Optimized for 10k+ tiles
Frontend: React 18 + Vite + Pixi.js 7 + TailwindCSS + Framer Motion Backend: Node.js + Express + Prisma ORM Database: PostgreSQL 15+ Storage: Local filesystem (images/drawings/audio)
# Start DB
docker-compose up postgres -d
# Backend
cd backend && npm install
cp .env.example .env
npx prisma migrate dev && npx prisma generate
npm run dev # Port 5000
# Frontend (new terminal)
cd frontend && npm install
cp .env.example .env
npm run dev # Port 5173Access: http://localhost:5173
User → React UI → Axios → Express API → Prisma → PostgreSQL
↓
File System (uploads/)
Frontend:
InfiniteWall.jsx- Pixi.js viewport with click detectionContributeCanvas.jsx- Multi-step form with file uploadlib/tilePositioning.js- Spiral algorithm (matches backend)lib/pixiRenderer.js- Tile sprite creation
Backend:
contributionService.js- Position calculation + collision detectionfileService.js- Image/audio processing with SharpidGenerator.js- Unique ID generation (short-unique-id)
model Contribution {
id String @id @default(uuid())
shortId String @unique // HM-XXXXX
x Float // Spiral position
y Float
type ContributionType // TEXT | DRAWING | IMAGE | AUDIO
content String? // For text
imagePath String? // /uploads/images/...
drawingPath String? // /uploads/drawings/...
audioPath String? // /uploads/audio/...
status Status @default(APPROVED)
ipAddress String?
createdAt DateTime @default(now())
}Tiles are positioned in a spiral pattern starting from (0,0):
Position 0: (0, 0)
Position 1: (0, -150)
Position 2: (150, -150)
Position 3: (150, 0)
...
TILE_SIZE: 150px with 10px visual padding in frontend only.
Collision Detection: Backend checks all existing positions before assigning coordinates.
Base URL: http://localhost:5000/api
POST /contributions # Create contribution
GET /contributions # List all (paginated)
GET /contributions/:shortId # Get by ID
GET /contributions/stats # Get statisticsPOST /contributions
Content-Type: multipart/form-data
type: TEXT | DRAWING | IMAGE | AUDIO
content: string (for TEXT)
drawing: dataURL (for DRAWING)
image: file (for IMAGE, max 5MB)
audio: file (for AUDIO, max 10MB)Response:
{
"success": true,
"message": "Contribution created successfully",
"data": {
"id": "uuid",
"shortId": "HM-ABC123",
"x": 0,
"y": 0,
"type": "TEXT",
"createdAt": "2024-01-15T10:30:00.000Z"
}
}Rate Limit: 15 requests per 15 minutes per IP.
NODE_ENV=development
PORT=5000
DATABASE_URL="postgresql://user:pass@localhost:5433/human_monument"
CORS_ORIGIN=http://localhost:5173
MAX_IMAGE_SIZE=5242880 # 5MB
MAX_AUDIO_SIZE=10485760 # 10MB
MAX_DRAWING_SIZE=2097152 # 2MB
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=15VITE_API_URL=http://localhost:5001/api# View database
npx prisma studio
# Reset database
npx prisma migrate reset
# Format code
npm run format
# Lint
npm run lint-
New contribution type:
- Update
ContributionTypeenum inprisma/schema.prisma - Add handler in
contributionService.js - Create UI in
ContributeCanvas.jsx - Add renderer in
pixiRenderer.js
- Update
-
New API endpoint:
- Add route in
backend/src/routes/contributions.js - Add controller method
- Add validation rules
- Add route in
See CONTRIBUTING.md for guidelines.
Quick checklist:
- Branch from
main - Follow existing code style
- Test locally
- Write clear commit messages
- Open PR with description
- Pixi.js handles 10k+ tiles at 60fps
- Viewport culling recommended beyond 50k tiles
- Database indexes on x, y, shortId for fast queries
- Sharp optimizes images on upload
- Rate limiting prevents abuse
- Drag conflicts with tile clicks on some touch devices
- Free tier backend (Render) sleeps after 15min inactivity
- File storage is ephemeral on free hosting (migrate to S3 for production)
MIT - See LICENSE
Built with ❤️ by the open-source community.
Core Technologies:
- Pixi.js - Canvas rendering
- pixi-viewport - Pan/zoom
- Prisma - ORM
- Vite - Build tool
⭐ Star this repo if you find it useful!