{"id":38030594,"url":"https://github.com/zfedoran/go-wfc","last_synced_at":"2026-01-18T03:03:12.122Z","repository":{"id":37422721,"uuid":"477181265","full_name":"zfedoran/go-wfc","owner":"zfedoran","description":"Randomly generated tile maps using Oskar Stålbergs wave function collapse algorithm","archived":false,"fork":false,"pushed_at":"2024-02-07T19:57:05.000Z","size":1623,"stargazers_count":44,"open_issues_count":2,"forks_count":9,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-17T05:37:58.400Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/zfedoran.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":"2022-04-02T22:02:53.000Z","updated_at":"2026-01-13T11:23:11.000Z","dependencies_parsed_at":"2024-02-07T20:44:55.805Z","dependency_job_id":"6aad3333-c5f1-463e-9252-9ac2c4190905","html_url":"https://github.com/zfedoran/go-wfc","commit_stats":{"total_commits":28,"total_committers":1,"mean_commits":28.0,"dds":0.0,"last_synced_commit":"ef80fe061d6e04a4e19a71863febdf048712ed35"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/zfedoran/go-wfc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfedoran%2Fgo-wfc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfedoran%2Fgo-wfc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfedoran%2Fgo-wfc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfedoran%2Fgo-wfc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zfedoran","download_url":"https://codeload.github.com/zfedoran/go-wfc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zfedoran%2Fgo-wfc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28528026,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"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":[],"created_at":"2026-01-16T19:40:04.353Z","updated_at":"2026-01-18T03:03:12.092Z","avatar_url":"https://github.com/zfedoran.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Go Reference](https://pkg.go.dev/badge/github.com/zfedoran/go-wfc/pkg/wfc.svg)](https://pkg.go.dev/github.com/zfedoran/go-wfc/pkg/wfc)\n[![Go Report Card](https://goreportcard.com/badge/github.com/zfedoran/go-wfc)](https://goreportcard.com/report/github.com/zfedoran/go-wfc)\n\n# go-wfc\nProcedurally-generated tile maps using wave function collapse. \n\n## Demos\n\nLive demo (wasm):\n* https://zfedoran.github.io/go-wfc-example/\n\nLive algorithm animation (wasm):\n* https://zfedoran.github.io/go-wfc-algorithm/\n\n\n## Overview\nThis package uses the *Wave Function Collapse* algorithm as described by [Oskar\nStålberg](https://www.youtube.com/watch?v=0bcZb-SsnrA\u0026t=350s).\n\nThe wave function collapse algorithm is a recursive algorithm that picks a\nrandom tile for a slot on the output image and removes impossible neighbors\nuntil only a single possibility remains. The algorithm is covered in more \ndetail [below](#algorithm).\n\nIf you need a generic algorithm, then please check out the golang fork of \nthe [original work](https://github.com/shawnridgeway/wfc).\n\n\u003cimg src=\"/doc/images/banner.jpg?raw=true\" width=\"80%\"\u003e\n\n## Why?\n\nThere is already a wfc golang library so why another one? The existing one is a\nlot more generic and quite a bit slower as a result. Also, the tile setup for\nthe original implementation can be very tedious. Additionally, I found it hard\nto follow and modify. \n\nThis variation follows Oskars work and aims to simplify the original work for\neasy integration into games.\n\n## Tiles\n\nYou'll need a set of tiles, also referred to as `modules`. These will \nneed to be designed to fit together. The tiles should have matching \ncolors along edges that should appear next to each other in the final \noutput. Other than that, no manual setup or description files are needed.\n\nYou should reference the [example folder](/example/) when making a tile set. \n\n![sample](/doc/images/tiles.png?raw=true)\n\n* Any number of tiles can be used as input, but the recommendation is to start\nwith a small set until you're comfortable with the constraint system.\n\n* You can create alternate tiles, meaning tiles with the same sets of colors\nalong all four edges. The probability for each alternate tile is the same. If\nyou'd like to increase/lower the probability of a particular tile, simply\nduplicate the image reference when calling the initialize method.\n\n* Unlike the original WFC implementation, no manual setup or description files\nare needed.\n\n## Adjacencies / Constraints\n\nThe wave function collapse algorithm requires some kind of adjacency mapping in\norder to remove impossible tiles and stitch together a possible output using the\ninput set.\n\nBy default, the package uses the color values along the four edges of each tile\n(`Up`, `Down`, `Left`, `Right`) to build constraints. But, you can\n[customize](#custom-constraints) this behaviour if you'd like.\n\n\u003cimg src=\"/doc/images/constraints.jpg?raw=true\" width=\"50%\"\u003e\n\nWhen designing your tiles, think about how the color values line up. They should\nbe exactly the same on the middle 3 points for two potentially adjacent tiles.\nFor example, the following tiles could appear as shown below because they share\nthe same colors on the bottom of the first and the top of the second.\n\n\u003cimg src=\"/doc/images/adjacencies.jpg?raw=true\" width=\"50%\"\u003e\n\nYou can view the default adjacency constraint implementation\n[here](https://github.com/zfedoran/go-wfc/blob/main/pkg/wfc/constraint.go#L36). \nIt scans colors along each edge of each input tile. These colors are turned into\na hash that represents that edge. Any tiles that have the same hash value in the\nopposite direction are considered possible adjacencies automatically.\n\n\n## Contradictions\n\nIt is possible that the wave can collapse into a state that has a contradiction.\nFor example, sky beneath the ground. If a contradiction is found, the algorithm\nre-tries until the maximum number of attempts is reached.\n\nWhen exporting an image, if you see a red tile, you've got a contradiction. If\nyou keep seeing these, your tileset likely has an issue.\n\n\u003cimg src=\"/doc/images/contradiction.png?raw=true\" width=\"50%\"\u003e\n\n## Results\n\nHere are some example outputs for a 8 x 8 grid.\n\n![results](/doc/images/permutations.jpg?raw=true)\n\n\n## Quick Start\n\nYou'll need to load a set of tiles (images) into an array. A convenience\nfunction is provided by this package but you can use any method you'd like.\n\n```go\n  // Load the input tile images (any order and count is fine)\n  var input_images []image.Image\n  input_images, err = wfc.LoadImageFolder(tileset_folder)\n  if err != nil {\n    panic(err)\n  }\n```\n\nNext, initialize a wave function with the desired output size (in units of\ntiles). For example, lets say that you want your output image to be 32 x 8\ntiles, you'd pass in the following.\n\n```go\n  // Setup the initialized state. The output image will be 32 x 8 tiles.\n  wave := wfc.New(input_images, 32, 8)\n  wave.Initialize(42) // seed: 42\n```\n\nFinally, collapse the wave into a single state (if possible).\n\n```go\n  // Collapse the wave function (make up to 100 attempts)\n  err = wave.Collapse(200)\n  if err != nil {\n    panic(err)\n  }\n```\n\nOptionally, you can export the collapsed wave to an image.\n\n```go\n  // Lets generate an image\n  output_image := wave.ExportImage()\n  wfc.SaveImage(\"wave.png\", output_image)\n```\n\nOr, you can review the results manually to do custom rendering in your game.\n\n```go\n  for _, slot := range wave.PossibilitySpace {\n    if len(slot.Superposition) == 1 {\n      // successfully collapsed slot\n      ...\n    }\n    if len(slot.Superposition) == 0 {\n      // contradiction\n      ...\n    }\n  }\n```\n\n## Full example:\n\n```go\nimport \"github.com/zfedoran/go-wfc/pkg/wfc\"\n\nfunc collapseWave(tileset_folder, output_image string) {\n  // This is just a `[]image.Image`, you can use whatever loader function you'd like\n  images, err := wfc.LoadImageFolder(tileset_folder)\n  if err != nil {\n    panic(err)\n  }\n\n  // The random seed to use when collapsing the wave\n  // (given the same seed number, the Collapse() fn would generate the same state every time)\n  seed := int(time.Now().UnixNano())\n\n  // Setup the initialized state\n  wave := wfc.New(images, 32, 8)\n  wave.Initialize(seed)\n  \n  // Collapse the wave function (make up to 100 attempts)\n  err = wave.Collapse(200)\n  if err != nil {\n    // don't panic here, we want to generate the image anyway\n    fmt.Printf(\"unable to generate: %v\", err)\n  }\n\n  // Lets generate an image\n  output := wave.ExportImage()\n  wfc.SaveImage(output_image, output)\n\n  fmt.Printf(\"Image saved to: %s\\n\", output_image)\n}\n```\n\nComplete source can be found here:\n[example/main.go](example/main.go)\n\nAlso, check out the animated version:\nhttps://github.com/zfedoran/go-wfc-example\n\n\n## Custom Constraints\nIf you'd like to customize or change this logic, you are able to pass in a\ncustom constraint function.\n\nYou can choose a different number of lookup points (3 is the default). For\nexample, 2 lookup points.\n\n```go\n  wave.NewWithCustomConstraints(tiles, width, height, wfc.GetConstraintFunc(2))\n```\n\nOr, you can provide your own.\n\n```go\n  wave.NewWithCustomConstraints(tiles, width, height, \n    func(img image.Image, d Direction) ConstraintId {\n    ...\n  })\n```\n\n## Algorithm\n\nThe algorithm is covered in detail here:\nhttps://www.youtube.com/watch?v=0bcZb-SsnrA\u0026t=350s\n\n1) A set of input image tiles (or modules) are loaded into memory. \n2) A wave function is defined with the desired output width and height (in\nunits of tiles).\n3) The wave function is initialized such that each output tile (or slot) is in a\nsuperposition of all provided input tiles.\n4) A random slot is selected and collapsed into a random input tile.\n5) Each of the neighboring slots is now evaluated to verify if there are any\ninput tiles that can fit next to the collapsed tile. Any impossible tiles\n(or modules) are removed.\n6) If the state of any of the neighboring tiles was changed in step 5), then\nrecurse into it's neighbors to remove impossible tiles.\n7) If there are no possible tiles left at any point, a contradiction has been\nfound and we need to go back to step 3) and try again.\n8) Once no more changes are left to propagate, go to step 4) and recurse until\nall slots are collapsed to a single state.\n\nOr, you if you prefer, here is the [actual implementation](https://github.com/zfedoran/go-wfc/blob/main/pkg/wfc/wave.go#L109).\n\n## Artwork\n\nThe awesome artwork in this repository was done by\n[@makionfire](https://twitter.com/makionfire). If you need help designing a tile\nset, I highly recommend reaching out to her. A huge shout-out to `@makionfire`\nfor letting me use this tileset.\n\nThe artwork itself does **not** fall under the MIT licence.\n\n## Licence\n\nThe licence for the source code in this package is MIT. Meaning, do whatever\nyou'd like but we'd love a shoutout. The goal is to get more folks to build\ngames with golang.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzfedoran%2Fgo-wfc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzfedoran%2Fgo-wfc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzfedoran%2Fgo-wfc/lists"}