{"id":23393591,"url":"https://github.com/lilnasy/tic-tac-toe","last_synced_at":"2026-05-01T21:03:15.197Z","repository":{"id":258408706,"uuid":"869209376","full_name":"lilnasy/tic-tac-toe","owner":"lilnasy","description":"A quirky little multiplayer game built with websockets, Preact, and Astro. This is built to showcase my work adding websocket support to Astro, which has a reputation for \"static\" websites, but it can do so much more! Hosted on a Node.js server running on an Oracle Cloud instance.","archived":false,"fork":false,"pushed_at":"2025-01-27T03:11:19.000Z","size":674,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-11T22:35:38.347Z","etag":null,"topics":["astro","board-game","preact","realtime","websocket"],"latest_commit_sha":null,"homepage":"https://sillystring.party","language":"TypeScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lilnasy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-07T22:51:22.000Z","updated_at":"2025-12-23T17:44:40.000Z","dependencies_parsed_at":"2024-12-08T00:20:04.986Z","dependency_job_id":"4d2c8ee2-47cc-47e4-99e5-f2764cf747c1","html_url":"https://github.com/lilnasy/tic-tac-toe","commit_stats":null,"previous_names":["lilnasy/tic-tac-toe"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lilnasy/tic-tac-toe","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lilnasy%2Ftic-tac-toe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lilnasy%2Ftic-tac-toe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lilnasy%2Ftic-tac-toe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lilnasy%2Ftic-tac-toe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lilnasy","download_url":"https://codeload.github.com/lilnasy/tic-tac-toe/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lilnasy%2Ftic-tac-toe/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32512670,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-30T13:12:12.517Z","status":"online","status_checked_at":"2026-05-01T02:00:05.856Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astro","board-game","preact","realtime","websocket"],"created_at":"2024-12-22T05:29:16.235Z","updated_at":"2026-05-01T21:03:15.177Z","avatar_url":"https://github.com/lilnasy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=center\u003e⨯◯\u003c/h1\u003e\n\u003cp align=center\u003e\n  \u003cimg width=340 height=475 src=https://github.com/user-attachments/assets/c4ce6a24-a3f7-4cc7-a99f-12b12e5a7807 /\u003e\n\u003c/p\u003e\n\n\u003cp align=center\u003eA realtime web game built with websockets, Preact, and Astro.\u003c/p\u003e\n\n\n## 🔍 Browse Around...\n\n- [/](./pages/index.astro) serves as the main menu, allowing to start a new game and to join an existing game with a shared code.\n- [/connect](./pages/connect.ts) creates a websocket connection between the browser and the [\"lobby\"](./game/lobby.ts).\n- [/world/:worldName](./pages/world/[worldName].astro) supports shareable links to automatically join a game.\n\n\n## 📸 Screenshots\n\n\u003cp align=center\u003e\n  \u003cimg width=345 height=500 src=https://github.com/user-attachments/assets/65489140-b2ac-427b-8a95-80dc6dded4eb /\u003e\n  \u003cimg width=345 height=500 src=https://github.com/user-attachments/assets/48f366ed-a404-4fc3-90c5-3a8753e75cd0 /\u003e\n  \u003cimg width=345 height=500 src=https://github.com/user-attachments/assets/503a70e4-ae04-428a-8ec6-e2943c76375f /\u003e\n  \u003cimg width=345 height=500 src=https://github.com/user-attachments/assets/1656002a-47e8-49a5-aea8-3a14a23c8e13 /\u003e\n\u003c/p\u003e\n\n## 🕸️ Built on the Platform\n- 🪄 [@starting-style](https://developer.mozilla.org/en-US/docs/Web/CSS/@starting-style) for animating microinteractions.\n- 🖌️ [OKLCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) for visually consistent color palettes.\n- 🗄️ [IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) for saving player details and selected theme.\n- 📡 [Service workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) to prevent flash of \"unthemed\" content.\n\n## 🏋️ Lightweight Libraries for the Heavylifting\n- 🎨 [Emotion](https://emotion.sh/docs/introduction) for styling, [astro-emotion](https://www.npmjs.com/package/astro-emotion) to generate all classes at build time.\n- 🔌 [astro-node-websocket](https://www.npmjs.com/package/astro-node-websocket) to add websocket support to Astro.\n- 🧮 [@preact/signals](https://preactjs.com/guide/v10/signals) for state management.\n- 🎊 [canvas-confetti](https://www.kirilv.com/canvas-confetti/) for victory celebrations.\n- 🧮 [clsx](https://github.com/lukeed/clsx) for composing CSS classes.\n\n## ✈️ Continuous Deployment\n\nUsing a GitHub Actions workflow and a bash script, changes are automatically deployed to the VPS running `sillystring.party`.\n- The project is [configured](./astro.config.ts#L18) to build into a self-contained static bundle, not dependent on `node_modules`.\n- [.github/workflows/deploy.yml](./.github/workflows/deploy.yml) builds the project, uploads the bundle as an [artifact](./.github/workflows/deploy.yml#L24), and [informs](./.github/workflows/deploy.yml#L33) the server of the artifact id.\n- The [server script](./Caddyfile#L32) downloads the artifact and restarts the node server.\n\n### 🔐 Secrets\n- The GitHub Actions workflow knows a deploy token, which is required by the server when requesting a redeploy.\n- The server knows a GitHub [access token](https://github.com/settings/personal-access-tokens), which is required by github.com when downloading artifacts.\n\n## 🪓 Hacking\n\n### 🚩 Getting Started\n\n1. Clone the repository:\n```bash\ngit clone https://github.com/lilnasy/tic-tac-toe\ncd tic-tac-toe\n```\n\n2. Install dependencies:\n```bash\npnpm install\n```\n\nTo start the development server:\n```bash\npnpm dev\n```\n\nThe application will be available at `http://localhost:4321`\n\n\n### 🏗️ Project Structure\n\n- 🎮 [/game](./game): Game logic and state management.\n- 📚 [/lib](./lib): Polyfills, state management helpers, IndexedDB persistence helpers, vite plugins.\n- 🎞️ [/assets](./assets): Sound effects and favicons.\n- 📃 [/pages](./pages): Astro file-based router.\n- 🧩 [/components](./components): UI and interaction logic.\n- 🛠️ [/patches](./patches): fixes to known issues in the libraries being used, and revert of preact's mangled code.\n\n### 🏭 Building for Production\n\nTo create a production build:\n```bash\npnpm build\n```\n\nStart the node server:\n```bash\nnode --enable-source-maps dist/server/entry.mjs\n```\n\nStart caddy to create TLS certificates, manage cache, and serve the application:\n```bash\ncaddy run --config ./Caddyfile\n```\n\n#### 🛫 Deploying as a service\n\nAutorestart the server on failure, manage logs, and start the server on every boot by creating an OpenRC service.\n\nCreate the OpenRC service file in `/etc/init.d/tictactoe`:\n\n```bash\n#!/sbin/openrc-run\nsupervisor=\"supervise-daemon\"\nname=\"Tic Tac Toe\"\ndescription=\"Run a node server hosting the game.\"\ncommand=\"node\"\n# adjust as necessary\ncommand_args=\"--enable-source-maps /app/dist/server/entry.mjs\"\ncommand_background=true\npidfile=\"/run/tictactoe.pid\"\noutput_log=\"/var/log/tictactoe.log\"\nerror_log=\"/var/log/tictactoe.err\"\n```\n\nStart the service:\n```bash\nservice tictactoe start\n```\n\nCheck uptime:\n```bash\nrc-status\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flilnasy%2Ftic-tac-toe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flilnasy%2Ftic-tac-toe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flilnasy%2Ftic-tac-toe/lists"}