{"id":17791313,"url":"https://github.com/schell/the-mutant","last_synced_at":"2026-02-07T15:32:28.229Z","repository":{"id":66260960,"uuid":"184485107","full_name":"schell/the-mutant","owner":"schell","description":"Don't fear the Mutant","archived":false,"fork":false,"pushed_at":"2019-10-06T13:53:57.000Z","size":145,"stargazers_count":3,"open_issues_count":3,"forks_count":0,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-07-19T03:50:49.503Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/schell.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":"2019-05-01T21:38:02.000Z","updated_at":"2020-01-20T03:12:59.000Z","dependencies_parsed_at":null,"dependency_job_id":"3d2b8d5e-d00a-4c93-b832-baf765d0bf94","html_url":"https://github.com/schell/the-mutant","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/schell/the-mutant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schell%2Fthe-mutant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schell%2Fthe-mutant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schell%2Fthe-mutant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schell%2Fthe-mutant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/schell","download_url":"https://codeload.github.com/schell/the-mutant/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/schell%2Fthe-mutant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29198178,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T14:35:27.868Z","status":"ssl_error","status_checked_at":"2026-02-07T14:25:51.081Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-10-27T10:50:35.346Z","updated_at":"2026-02-07T15:32:28.206Z","avatar_url":"https://github.com/schell.png","language":"Haskell","readme":"# The Mutant\nThis is a framework of game related APIs and backends for aspiring Haskell game enthusiasts.\n\n## APIs\n\n- [x] [2d graphics](https://github.com/schell/the-mutant/blob/master/src/Mutant/API/Render2d.hs)\n- [ ] [input events](https://github.com/schell/the-mutant/blob/master/src/Mutant/API/Events.hs)\n- [ ] audio\n- [ ] 2d physics\n- [ ] 3d gles graphics\n\n## Backends\n\n- [x] Linux\n- [x] macOS\n- [x] Windows\n- [x] browser\n- [ ] iOS\n- [ ] Android\n\n## Background\n*The Mutant* picks up where other projects like [gelatin][gelatin], [glucose][glucose] and\n[odin][odin] left off. My goal is to bring back some of the cool features from\nthose failed projects and get them up and running again in a slightly new context.\nThe point of it all is to be able to quickly write portable video games and multimedia\napplications in Haskell.\n\n*The Mutant* gets its name from its unapologetic use of in-place mutation, which\nmany Haskell devs fear.\n\nDon't fear The Mutant.\n\nSimilarly, it \"mutates\" your game onto a\nnumber of target platforms - the name is a double entendre. It also lends itself to cool\nicons.\n\nDon't fear The Mutant.\n\n[gelatin]: https://github.com/schell/gelatin\n[glucose]: https://github.com/schell/glucose\n[odin]: https://github.com/schell/odin\n\n## Examples\n\nThe various APIs are defined using records. Records live at an interesting point\nin the solution space between typeclasses and effects. Unlike both typeclasses\nand many effect systems, records have great type inference. There is no need to\ntype apply any of the API functions in order to use them. Unlike many effect\nsystems, records are fast and there are no new concepts or puzzles to solve in\norder to run your computations - just pass the API to the function that needs them.\n\nWith records, just like typeclasses - you get an abstract API. Similar to an\neffects system you get to see what \"effects\" are likely happening within your\ncomputation simply by viewing the type signature.\n\n### Rendering\n\n```haskell\n{-# LANGUAGE RecordWildCards #-}\n\nimport Mutant.API.Render2d\nimport Mutant.Geom\n\ndrawingStuff\n  :: MonadIO m\n  =\u003e Render2dAPI i m\n  -\u003e m ()\ndrawingStuff Render2dAPI{..} = do\n  wh@(V2 w h) \u003c- getDimensions\n  liftIO $ putStrLn $ \"Context has dimensions \" ++ show wh\n  let tl = 10\n      tr = V2 (w - 10) 10\n      br = V2 (w - 10) (h - 10)\n      bl = V2 10 (h - 10)\n      lns = [Line tl br, Line tr bl]\n      grey = V4 174 174 174 255\n      black = V4 255 255 255 255\n      white = V4 255 255 255 255\n      cyan = V4 0 255 255 255\n      canary = V4 255 255 0 255\n\n  clear\n\n  -- first draw the screen grey\n  setDrawColor grey\n  fillRect\n    $ Rect 0 wh\n\n  -- draw a white frame inset by 10 pixels\n  setDrawColor white\n  strokeRect\n    $ insetRect (Rect 0 wh) 10\n\n  -- then draw a cyan X\n  setDrawColor cyan\n  traverse_ strokeLine lns\n\n  -- load an image, get its size\n  tex \u003c- await =\u003c\u003c texture \"sot.png\"\n  tsz@(V2 _ th) \u003c- textureSize tex\n  liftIO $ putStrLn $ \"Texture size is \" ++ show tsz\n\n  -- draw the texture to the screen\n  let posf :: V2 Float\n      posf = (fromIntegral \u003c$\u003e wh)/2.0 - (fromIntegral \u003c$\u003e tsz)/2.0\n      pos = floor \u003c$\u003e posf\n  fillTexture\n    tex\n    (Rect 0 tsz)\n    (Rect pos tsz)\n\n  -- do some higher-order drawing into the texture itself\n  withTexture tex $ do\n    texDims \u003c- getDimensions\n    liftIO $ putStrLn $ \"textures dimensions are:\" ++ show texDims\n    setDrawColor canary\n    fillRect (Rect 0 25)\n  fillTexture\n    tex\n    (Rect 0 tsz)\n    (Rect (pos + V2 0 th) tsz)\n\n  -- play with fonts and text\n  setDrawColor black\n  komika \u003c- await =\u003c\u003c font \"komika.ttf\"\n  fillText\n    komika\n    (V2 16 16)\n    (V2 100 100)\n    \"Here is some text...\"\n\n  -- present the window\n  present\n\n  -- loop so the window won't close on desktop\n  fix $ \\loop -\u003e do\n    liftIO $ threadDelay 1000000\n    loop\n```\n\n### Creating instances of an API\n\nThe various APIs all have creation functions provided by multiple backends.\nHere's an example of creating a \"mutant instance\" and the 2d rendering API and\npassing it to the previous `drawingStuff` function:\n\n```haskell\nimport Mutant.Backends.SDL\n\nmain :: IO ()\nmain = do\n  i \u003c- getSDLInstance \"My SDL Game\" (V2 640 480)\n  r \u003c- getSDLRender2dAPI i \"assetsDirectory/\"\n  drawingStuff r\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschell%2Fthe-mutant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fschell%2Fthe-mutant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fschell%2Fthe-mutant/lists"}