Skip to content

abeshahsan/playclassix

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

49 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PlayClassix

Classic multiplayer games reimagined with modern real-time technology

Next.js TypeScript Redis Pusher License

Live Demo β€’ Report Bug β€’ Request Feature


About

PlayClassix is a modern web platform bringing classic games to life with real-time multiplayer capabilities. Built with Next.js 16 and powered by serverless Redis, it offers seamless gameplay experiences with instant state synchronization across players.

Perfect for casual gaming sessions with friends, PlayClassix eliminates the need for downloads or installationsβ€”just share a link and start playing instantly in your browser.

Current Status: Memory Match is fully playable with 2-player real-time multiplayer. More games coming soon!


Key Features

  • No Login Required β€” Start playing instantly with auto-generated session IDs stored in browser cookies. No personal data collected, all session data cleared when browser closes
  • Real-Time Multiplayer β€” Play with friends instantly using Pusher WebSocket connections for sub-100ms state synchronization
  • Optimistic UI Updates β€” Cards flip immediately on click with automatic rollback on network failures for butter-smooth UX
  • Session Statistics β€” Track wins, losses, and draws with your opponents using serverless Redis persistence
  • Smart Turn Management β€” Race condition-free gameplay with processing locks preventing double-moves
  • Dark Mode Support β€” Fully responsive design with light/dark theme support and custom CSS variables
  • Serverless Architecture β€” Zero-latency deployment on Vercel with Upstash Redis for global performance
  • Accessibility First β€” Semantic HTML, ARIA labels, and keyboard navigation throughout

Technology Stack

Category Technologies
Framework Next.js 16 with App Router & React Server Components
Language TypeScript 5 with strict mode
Styling Tailwind CSS 4 with custom design system
State Management Zustand for client state
Database Upstash Redis for game state & session stats
Real-Time Pusher for WebSocket-based multiplayer sync
Icons React Icons (Feather set)
Fonts Geist Sans & Geist Mono
Deployment Vercel with Edge Runtime
Dev Tools ESLint 9, Sharp (image optimization)

Featured Games

Memory Match (Completed)

A classic memory card game reimagined for real-time multiplayer:

  • 16-card grid with fruit-themed illustrations
  • 2-player competitive gameplay
  • Real-time score tracking and turn indicators
  • Session-based statistics (W/L/D records)
  • Game complete modal with individual stats
  • Optimistic card flips with error recovery

Coming Soon

  • Wordle Clone β€” Daily word puzzle with multiplayer duels
  • Tic Tac Toe β€” Strategic multiplayer grid game
  • Snake β€” Classic arcade action
  • Chess β€” Timeless strategy game with real-time play

Architecture

PlayClassix follows a modern Next.js App Router architecture with clear separation of concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      Client Layer                       β”‚
β”‚  - React Components (Server + Client Components)        β”‚
β”‚  - Zustand Stores (Local State)                         β”‚
β”‚  - Custom Hooks (Business Logic)                        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    API Routes Layer                     β”‚
β”‚  - Server Actions (Next.js 16)                          β”‚
β”‚  - RESTful API endpoints                                β”‚
β”‚  - Cookie management (authentication)                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Services Layer                      β”‚
β”‚  - Game Store (game-store.ts)                           β”‚
β”‚  - Pusher Server (real-time events)                     β”‚
β”‚  - Redis Client (state persistence)                     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                             β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   External Services                     β”‚
β”‚  - Upstash Redis (Serverless DB)                        β”‚
β”‚  - Pusher Channels (WebSocket)                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Design Patterns

  • Optimistic Updates β€” Client updates UI immediately, rolls back on API failure
  • WebSocket Sync β€” Pusher broadcasts game state changes to all connected players
  • Redis as SSoT β€” All game state persists to Redis with 2-hour TTL
  • Custom Hooks β€” Game logic encapsulated in reusable hooks (useCardClickHandler, useSetUpPusherClient)
  • Type Safety β€” End-to-end TypeScript with strict compiler options

Getting Started

Prerequisites

Installation

  1. Clone the repository

    git clone https://github.com/abeshahsan/playclassix.git
    cd playclassix
  2. Install dependencies

    npm install
    # or
    pnpm install
  3. Configure environment variables

    Create a .env.local file in the root directory. see .env.example for required variables.

  4. Run development server

    npm run dev

    Open http://localhost:3000 in your browser.

  5. Build for production

    npm run build
    npm start

Project Structure

playclassix/
β”œβ”€β”€ public/
β”‚   β”œβ”€β”€ assets/
β”‚   β”‚   β”œβ”€β”€ cards/          # Game card images
β”‚   β”‚   β”œβ”€β”€ icons/          # App icons (favicon, apple-touch)
β”‚   β”‚   β”œβ”€β”€ logos/          # Brand logos
β”‚   β”‚   β”œβ”€β”€ ui/             # UI assets (medals, backgrounds)
β”‚   β”‚   └── social/         # OG images for social sharing
β”‚   └── manifest.json       # PWA manifest
β”‚
β”œβ”€β”€ scripts/
β”‚   └── generate-assets.mjs # Asset generation utilities
β”‚
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ api/            # API routes
β”‚   β”‚   β”‚   β”œβ”€β”€ games/      # Game-specific APIs
β”‚   β”‚   β”‚   β”‚   └── memory-match/
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ new-game/
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ join-game/
β”‚   β”‚   β”‚   β”‚       β”œβ”€β”€ move/
β”‚   β”‚   β”‚   β”‚       └── player-stats/
β”‚   β”‚   β”‚   β”œβ”€β”€ me/         # User authentication
β”‚   β”‚   β”‚   └── message/    # System messages
β”‚   β”‚   β”œβ”€β”€ games/          # Game pages
β”‚   β”‚   β”‚   └── memory-match/
β”‚   β”‚   β”‚       β”œβ”€β”€ [gameId]/
β”‚   β”‚   β”‚       └── new-game/
β”‚   β”‚   β”œβ”€β”€ set-username/   # Onboarding
β”‚   β”‚   β”œβ”€β”€ layout.tsx      # Root layout
β”‚   β”‚   └── page.tsx        # Home page
β”‚   β”‚
β”‚   β”œβ”€β”€ components/
β”‚   β”‚   β”œβ”€β”€ games/          # Game-specific components
β”‚   β”‚   β”‚   └── memory-match/
β”‚   β”‚   β”‚       β”œβ”€β”€ card.tsx
β”‚   β”‚   β”‚       β”œβ”€β”€ game-complete-modal.tsx
β”‚   β”‚   β”‚       β”œβ”€β”€ score-board.tsx
β”‚   β”‚   β”‚       └── ...
β”‚   β”‚   └── global/         # Shared components
β”‚   β”‚       β”œβ”€β”€ app-shell.tsx
β”‚   β”‚       β”œβ”€β”€ sidebar.tsx
β”‚   β”‚       β”œβ”€β”€ site-header.tsx
β”‚   β”‚       └── theme-provider.tsx
β”‚   β”‚
β”‚   β”œβ”€β”€ core/
β”‚   β”‚   └── game.registry.ts  # Game metadata registry
β”‚   β”‚
β”‚   β”œβ”€β”€ hooks/
β”‚   β”‚   └── games/
β”‚   β”‚       └── memory-match/
β”‚   β”‚           β”œβ”€β”€ useCardClickHandler.ts
β”‚   β”‚           β”œβ”€β”€ useJoinOrFetchGame.ts
β”‚   β”‚           └── useSetUpPusherClient.ts
β”‚   β”‚
β”‚   β”œβ”€β”€ lib/
β”‚   β”‚   β”œβ”€β”€ game-store.ts   # Redis-backed game state
β”‚   β”‚   β”œβ”€β”€ pusher.ts       # Pusher server config
β”‚   β”‚   └── redis.ts        # Redis client (Upstash/ioredis)
β”‚   β”‚
β”‚   β”œβ”€β”€ store/
β”‚   β”‚   β”œβ”€β”€ gamer.ts        # User state (Zustand)
β”‚   β”‚   └── games/
β”‚   β”‚       └── memory-match.ts  # Game-specific state
β”‚   β”‚
β”‚   └── types/
β”‚       β”œβ”€β”€ index.ts        # Global types
β”‚       └── games/
β”‚           └── memory-match/
β”‚               └── types.ts
β”‚
β”œβ”€β”€ .env.local              # Environment variables (not in git)
β”œβ”€β”€ next.config.ts          # Next.js configuration
β”œβ”€β”€ tailwind.config.ts      # Tailwind CSS config
β”œβ”€β”€ tsconfig.json           # TypeScript config
└── package.json            # Dependencies

Key Implementation Details

Redis State Management

Games are stored with a 2-hour TTL:

// Key pattern: game:memory-match:{gameId}
await redis.set(`game:memory-match:${gameId}`, JSON.stringify(gameState), "EX", 7200);

Player stats use sorted pair keys:

// Key pattern: player-stats:{player1Id}:{player2Id}
// IDs are sorted to ensure consistency
const key = [player1Id, player2Id].sort().join(":");

Optimistic Updates

  1. Client flips card immediately
  2. Send move to server
  3. On success: Pusher broadcasts to all players
  4. On failure: Rollback local state + show error
const previousState = { ...gameRoom };
optimisticFlipCard(cardId);  // Immediate UI update

const success = await sendMove(cardId, gameId, userId);
if (!success) {
  rollbackOptimisticFlip(previousState);  // Revert on error
}

Preventing Race Conditions

Processing lock prevents double-clicks:

if (isProcessing) return;  // Block new moves
setIsProcessing(true);     // Lock for 500ms minimum

Security & Environment Variables

Critical: Never commit sensitive keys to git!

Required environment variables:

  • UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN
  • PUSHER_APP_ID / PUSHER_SECRET / NEXT_PUBLIC_PUSHER_KEY / NEXT_PUBLIC_PUSHER_CLUSTER

Use .env.local for local development (already in .gitignore).

For production deployment on Vercel:

  1. Project Settings β†’ Environment Variables
  2. Add all required keys
  3. Redeploy

Contributing

Contributions are welcome! Standard flow:

  1. Fork the project
  2. Create your feature branch
    git checkout -b feature/amazing-feature
  3. Commit your changes
    git commit -m 'Add some amazing feature'
  4. Push to the branch
    git push origin feature/amazing-feature
  5. Open a Pull Request

Development Guidelines

  • Follow existing code style (TypeScript strict mode)
  • Add types for all new functions/components
  • Test multiplayer features with 2+ browser tabs
  • Update README if adding new features

License

Distributed under the MIT License. See LICENSE for more information.


Author

K. M. Abesh Ahsan
GitHub: @abeshahsan
Website: playclassix.com


Show Your Support

Give a star ⭐ if you like this project!


Built with ❀️ by K. M. Abesh Ahsan

Back to Top

About

Classic multiplayer games with real-time WebSocket sync. No login required; instant play with session-based IDs. Built with Next.js 16, Redis, and Pusher for sub-100ms state synchronization.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors