{"id":17721818,"url":"https://github.com/sinbad/spriterecolour","last_synced_at":"2026-02-27T12:44:23.893Z","repository":{"id":53927621,"uuid":"73396490","full_name":"sinbad/spriterecolour","owner":"sinbad","description":"Sprite processor to allow dynamic recolouring","archived":false,"fork":false,"pushed_at":"2016-11-12T10:56:58.000Z","size":717,"stargazers_count":26,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-06-19T01:59:43.479Z","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/sinbad.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}},"created_at":"2016-11-10T15:46:29.000Z","updated_at":"2022-08-13T03:41:02.000Z","dependencies_parsed_at":"2022-08-13T04:40:25.309Z","dependency_job_id":null,"html_url":"https://github.com/sinbad/spriterecolour","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/sinbad/spriterecolour","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2Fspriterecolour","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2Fspriterecolour/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2Fspriterecolour/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2Fspriterecolour/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sinbad","download_url":"https://codeload.github.com/sinbad/spriterecolour/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinbad%2Fspriterecolour/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29895531,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-27T12:09:13.686Z","status":"ssl_error","status_checked_at":"2026-02-27T12:09:13.282Z","response_time":57,"last_error":"SSL_read: 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-25T15:36:21.211Z","updated_at":"2026-02-27T12:44:23.862Z","avatar_url":"https://github.com/sinbad.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Sprite Recolour Map Generator\n\n## Introduction\n\nThis tool generates the base textures needed to easily recolour \nsprites flexibly on the fly in a shader, while preserving easy authoring of the \noriginal sprites.\n\nThere's a demo project in Unity using assets produced by this tool here:\n[Unity Sprite Recolouring Demo](https://github.com/sinbad/SpriteRecolourUnity).\n![Example](SpriteRecolourAnimated.gif)\n\n## How to use\n\nAn artist creates a sprite exactly as they usually would. This tool generates\nfrom that a derived \"reference\" sprite and a base palette, stored either as a\nseparate small texture or shader parameters. Combining the two in a shader\nre-creates the original appearance, and alternative colour palettes can swapped \nin at runtime by providing alternate palette textures or changing shader \nparameters.\n\n```\nUsage:\n  spriterecolour [options] \u003cinput file\u003e\n\nOptions:\n  -o, --output string    Output sprite file; default \u003cinput\u003e_reference.png\n  -t, --texture string   File to write palette as texture; default \u003cinput\u003e_palette.png\n  -p, --params string    File to write shader params to; default none\n                         Mutually exclusive with --texture\n  -b, --byte-params      When using --params, write values as 0-255 instead of 0.0-1.0-255\n```\n\n## Principle\n\n### Previous work\n\nThe core idea is an extension of [this blog post](https://gamedevelopment.tutsplus.com/tutorials/how-to-use-a-shader-to-dynamically-swap-a-sprites-colors--cms-25129). In that\ncase, the Red colour component of the original sprite is used to index a \ntexture containing the replacement colours. While this is fine, it requires that\nthe artist never uses 2 colours with the same Red value, which is overly \nlimiting.\n\n### What SpriteRecolour does differently\n\nSpriteRecolour allows the use of any input sprite, and performs these steps\n\n1. Analyse the image and identify all the unique colours\n2. Sort the colours into a colour palette ordered by perceptual distance (CIE76)\n3. This palette is saved to a palette texture; this is power-of-two sized, and\n   at most 256 wide. If more than 256 colours are needed the texture also grows\n   vertically in powers of 2.\n4. Write another sprite texture the same size as the original, which we call the\n   **reference sprite**. Set the colour components as follows:\n   * R = Colour index, which is either:\n     * U texture coord to palette texture (default if --params not used)\n     * Parameter index to shader parameter (if --params used)\n   * G = V index of colour in palette texture (if --params not used)\n   * B = Currently unused\n   * A = original alpha\n\nWhen rendering the sprite, we simply combine the **reference sprite** with \n*either* a modified palette texture or modified shader arguments to recolour it. \n\nThe recombination algorithm is:\n\n```\nout.rgb = GetPaletteColour(in.r, in.g);\nout.a = in.a;\n```\n\nWhere `GetPaletteColour` either samples a palette texture (no filtering /\nmipmapping) or indexes an array of shader constants containing the replacement\ncolour.\n\n### Differences when using palette textures/parameters\nThe values in the Red channel are different depending on whether you're using\na palette texture or shader parameters to specify the palette. \n\n1. When using a **palette texture**, the colour index is rescaled so that it\n   becomes a valid texture coordinate. If the palette texture is smaller than\n   256 pixels wide then this will be different to the colour index. The same\n   applies to the green channel if more than 256 colours are used.\n2. When using **shader parameters**, the values are always the unmodified index\n   of the colour. Because of this a maximum of 256 colours are allowed\n\n## Limitations\n\n### No filtering or mipmaps\n\nMipmapping or filtering doesn't work because the textures are no longer continuous,\nhaving unrelated values next to each other. Therefore you need to disable all\ntexture filtering and mipmaps for both the reference sprite and the palette\ntexture.\n\n### No lossy compression\n\nBecause the output reference sprite relies very heavily on correct indexing \nusing the Red channel, lossy compression cannot be supported. The reference\nsprite is always output in PNG format right now, and you should not convert it\nto a lossy compressed format such as JPG or (sadly) ETC/DXT/S3TC. \n\n#### Example: texture settings in Unity\nClick on the sprite texture, and in the Inspector, check the \"Override for..\"\nbox and set the format to \"Truecolor\" (or in Advanced mode, \"ARGB 32 bit\"). \n\nYou probably also want to make sure that the Packing Tag for the sprite is set\nto a value that is only shared by other sprites using recolouring.\n\nNote: if you're using a texture as the recolour palette, make sure you import\nthis into Unity as a regular texture and that it's not a Sprite, you don't want\nto pack it with other sprites. You can use compression for this texture if you\nlike but watch out for artefacts if adjacent hues are very different.\n\n### L8A8 support\n\nWhere the number of colours in the input sprite is \u003c= 256, we could encode it in\nthe reference sprite as just 8 bits of luminance and the alpha, i.e. L8A8 rather\nthan RGBA. The only reason I'm not doing this is that I found out too late that\nGo's image package doesn't support LA formats, only RGB[A] and plain L \n(greyscale).\n\nYou can work around this by post-processing using ImageMagick:\n\n```\nconvert -separate -channel RA -combine sprite_reference.png sprite_reference_la.png\n```\n\nThe output `sprite_reference_la.png` only uses 2 channels then, which in \nuncompressed texture memory is half the size (the PNG will only be a bit smaller\nbecause of its compression but at runtime it will be uncompressed). It's only \nworth doing this if the engine you're using supports L8A8 textures;  for example\nas far as I could tell Unity still converts L8A8 PNGs to RGBA textures \ninternally, so you don't save anything in practice. Other engines may support\nL8A8 natively (e.g. I know [Ogre](http://ogre3d.org) does)\n\n### Premultiplied alpha\n\nPremultiplied alpha is not supported right now, because the palette RGB and\nalpha are stored separately. If you use an input texture with premultiplied\nalpha then things may kind of work; but if alpha varies per reference colour\nyou'll get more colours in the output palette (since say red=200 is 200 at full\nalpha and 100 at half-alpha, which looks like different colours to this tool)\nso it won't be as easy to supply replacement palettes.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinbad%2Fspriterecolour","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinbad%2Fspriterecolour","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinbad%2Fspriterecolour/lists"}