{"id":17229862,"url":"https://github.com/josephg/sephsplace","last_synced_at":"2025-03-17T11:30:30.947Z","repository":{"id":66052006,"uuid":"88238003","full_name":"josephg/sephsplace","owner":"josephg","description":"My own version of r/place, done in a weekend","archived":false,"fork":false,"pushed_at":"2017-04-17T04:57:13.000Z","size":57,"stargazers_count":130,"open_issues_count":0,"forks_count":15,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-27T23:05:12.861Z","etag":null,"topics":["cqrs","event-sourcing","hackathon","kafka"],"latest_commit_sha":null,"homepage":"https://josephg.com/sp/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josephg.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2017-04-14T06:02:26.000Z","updated_at":"2024-08-28T07:58:53.000Z","dependencies_parsed_at":"2023-04-01T10:02:42.289Z","dependency_job_id":null,"html_url":"https://github.com/josephg/sephsplace","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephg%2Fsephsplace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephg%2Fsephsplace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephg%2Fsephsplace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josephg%2Fsephsplace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josephg","download_url":"https://codeload.github.com/josephg/sephsplace/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243858056,"owners_count":20359271,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["cqrs","event-sourcing","hackathon","kafka"],"created_at":"2024-10-15T04:51:21.956Z","updated_at":"2025-03-17T11:30:30.931Z","avatar_url":"https://github.com/josephg.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Reddit r/place in a weekend throwdown!\n\nSoooo, I said I could implement r/place (minus mobile support) in a weekend.\nSomeone called me on it, so I did it.\n\n[Go check it out](https://josephg.com/sp)\n\n[Postmortem writeup](https://josephg.com/blog/rplace-in-a-weekend/)\n[Original HN thread](https://news.ycombinator.com/item?id=14112085)\n\n\n## Design\n\n\u003e This was written when I started the project. You're better off taking a look\n\u003e at the [postmortem blog post](https://josephg.com/blog/rplace-in-a-weekend/)\n\u003e for details about how it works.\n\nThe actual image edits will all be sent to Kafka. Each server (well, kafka client) will host a snapshot of the place image which it will serve.\n\nNow, the entire image will actually be kind of big and slow to png-encode. We\ncan't re-encode it all the time. So instead I'm going to only regenerate the\nimage every 100 edits or something and aggressively cache it in nginx.\n\nThat means the client will see an old version of the image. To get around that\n(and to allow people to see pixels change in realtime) I'm going to add a\nserver-sent events endpoint which will allow the client to subscribe from a\ngiven image version.\n\nSo:\n\nReads:\n\n- Client gets image, which may be a bit old. Image fetch hits nginx, which will\nrespond with a cached copy 99% of the time. Response header contains image\nversion (eg, 1000). If the image is not in cache it is generated in the node\nserver.\n- Client hits `/edits?v=1000` or something. They get back a stream of edits\nfrom the specified version. The client applies those edits locally to the image\nthey downloaded.\n\nWrites:\n\n- Client sends a POST request with the desired edit (x, y, value) to `/edit`.\nServer sends message to kafka.\n- Client sees their own write once they get back a message from the\neventstream. If I have time I'll make their edit speculatively visible in the\nclient before they see it in the server data.\n\nUsing this architecture I can spin up as many servers as I want to handle the\nload. Writes are all sent through kafka, which can handle millions of edits\nper second.\n\nWhen the server starts it needs to load the current data. Instead of replaying\nthe entire kafka event log, the client will store a local snapshot in lmdb\ncontaining a recent copy of the image data.\n\nThere's a fair few moving parts that all need to be kept in sync: kafka,\nserver, client, lmdb. But all the data flows one way (client -\u003e server -\u003e\nkafka -\u003e client) and ({lmdb, kafka} -\u003e server -\u003e {lmdb, kafka}). So it should\nbe mostly straightforward. I hope.\n\n\n---\n\n# License\n\nISC License\n\nCopyright (c) 2017 Joseph Gentle\n\nPermission to use, copy, modify, and/or distribute this software for any\npurpose with or without fee is hereby granted, provided that the above\ncopyright notice and this permission notice appear in all copies.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH\nREGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\nAND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,\nINDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\nLOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\nOR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\nPERFORMANCE OF THIS SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosephg%2Fsephsplace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosephg%2Fsephsplace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosephg%2Fsephsplace/lists"}