{"id":22542506,"url":"https://github.com/maumagnaguagno/spriter","last_synced_at":"2026-02-15T01:32:54.655Z","repository":{"id":28024491,"uuid":"31519656","full_name":"Maumagnaguagno/Spriter","owner":"Maumagnaguagno","description":":space_invader: Sprite generator","archived":false,"fork":false,"pushed_at":"2026-01-13T10:02:35.000Z","size":132,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-13T12:57:37.311Z","etag":null,"topics":["image","ruby","sprite"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"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/Maumagnaguagno.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2015-03-02T02:20:06.000Z","updated_at":"2026-01-13T10:02:38.000Z","dependencies_parsed_at":"2024-08-01T13:01:13.675Z","dependency_job_id":"a60e5a21-7d57-43cc-9108-d00a769c2b04","html_url":"https://github.com/Maumagnaguagno/Spriter","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Maumagnaguagno/Spriter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maumagnaguagno%2FSpriter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maumagnaguagno%2FSpriter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maumagnaguagno%2FSpriter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maumagnaguagno%2FSpriter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Maumagnaguagno","download_url":"https://codeload.github.com/Maumagnaguagno/Spriter/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Maumagnaguagno%2FSpriter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29464126,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-15T01:01:38.065Z","status":"ssl_error","status_checked_at":"2026-02-15T01:01:23.809Z","response_time":53,"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":["image","ruby","sprite"],"created_at":"2024-12-07T13:10:42.906Z","updated_at":"2026-02-15T01:32:54.607Z","avatar_url":"https://github.com/Maumagnaguagno.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spriter [![Actions Status](https://github.com/Maumagnaguagno/Spriter/workflows/build/badge.svg)](https://github.com/Maumagnaguagno/Spriter/actions)\n**Sprite generator based on monochrome [Spritegen](https://web.archive.org/web/20160305123432/http://tools.putpixel.com/spritegen.html)**\n\u003ca href=\"/sprites/svg/sprite_65.svg\" target=\"_blank\"\u003e\n\u003cimg src=\"/sprites/svg/sprite_65.svg\" align=\"right\" width=\"96\" title=\"Puma\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"/sprites/svg/sprite_64.svg\" target=\"_blank\"\u003e\n\u003cimg src=\"/sprites/svg/sprite_64.svg\" align=\"right\" width=\"96\" title=\"Old man\"/\u003e\n\u003c/a\u003e\n\nI wanted cool sprites, but creative blocks are always haunting me...\nSoon I found an online tool generating several small sprites and thought: **Cool, but it is not Ruby...** :broken_heart:  \nInspecting the element I found the source with the [WTFPL](http://www.wtfpl.net/), and got started.  \nA [new colored version of Spritegen is available here](https://web.archive.org/web/20221016034741/https://img.uninhabitant.com/spritegen.html).\n\nIt was December 2013, created my own version with most of the algorithm intact to test my Image class.\nWith integer probability and seed control it was easy to handle the beast.\nSome time after that I moved most of the Image class to C to achieve enough speed for in-game effects and left the old pure Ruby class waiting.\nTherefore, the project contains an Image class split in two files, [Image.rb](Image.rb) with basic methods and [ImageX.rb](ImageX.rb) extending the Image class with complex methods.\nThe Spriter class defined in [Spriter.rb](Spriter.rb) make use of the Image class.\n\nThe goal is not to have a Sprite generator alone, but a consistent example of an Image class being used to save and load files.\nIt is a shame that image and sound are not first class citizens of modern languages, requiring a lot of work to build a library or to understand one to reach this level of fun.\n\n## Image class\nBefore you have sprites, you need images.\nAnd since BMP is my favorite image format (much simpler), pixels are stored as BGRA32 internally to hold the channels in a packed String, e.g. an image with 3 pixels is stored as ``BGRABGRABGRA``.\nEach 8 bit channel holds a color: Red, Green, Blue and Alpha.\nAlpha is used for transparency effects, usually used in PNGs.\nWe can easily pack an Array ``color = [B,G,R,A]`` to a BGRA32 String using ``color.pack('C4')``, where B, G, R and A are Integers between 0 and 255.\n\n```Ruby\nrequire_relative 'Image'\nrequire_relative 'ImageX'\n\nwidth = height = 32\ndata = [0, 0, 255, 255].pack('C4') * (width * height)\nimg = Image.new(width, height)\nimg.write(0, 0, data, data.size) # Fill image with red BGRA32\nimg.save_bmp('red.bmp') # BMPs are uncompressed, eat HD (3.05 KB)\nimg.save_png('red.png') # PNGs are compressed, eat CPU (96 bytes)\n# SVGs are vector based, CPU and HD usage varies\nimg.save_svg('red1.svg') # Without background (470 bytes)\nimg.save_svg('red2.svg', 255, 0, 0) # With red background (96 bytes)\n```\n\nYou can also load BMPs and PNGs, note that only BMPs with 24 bits per pixel (8 bits per channel) are supported, therefore palette based ones are up to you to implement.\nIt supports two modes for PNG: RGB and RGBA.\n\nIf any part of my implementation is not understandable at first sight, you should read this [guide to the binary format of BMPs](https://practicingruby.com/articles/binary-file-formats).\nI just optimized further for my specific need of BMPs.\nAnother interesting post is [ChunkyPNG pack/unpack tricks](https://web.archive.org/web/20200202031423/https://chunkypng.com/2010/01/17/ode-to-array-pack-and-string-unpack.html) related to images stored as an Array of Integers.\n\nThe naive conversion to SVG resulted in big files describing each pixel as a rectangle.\nThe new method sets a background color and cluster consecutive equal pixels.\nSuch optimization may help decrease the file size while saving time, as writing a big file to disk is much slower.\nNote that only images with limited palette can exploit such optimization.\n\n## Spriter class\nOk, so now we can play with images.\nBut none of us know how to draw, even less without an interface.\nWe are not alone, the computer also does not know.\nNow the three of us make the non-artist club.\nWe know that sometimes we draw OK, but it is one in ten and it takes a long time to draw.\nThe computer can draw much faster those ten times.\nHumm...\nWhat if we asked the computer for a lot of images with different random seeds and pixel distribution probabilities.\nIt works, the computer drew something that resembles a Puma!\nCreativity is just brute-force with insight now!\nWe can look at the generated images and complete the missing pixels.\n\nIt is important to note that you can reproduce your results by giving the same ~~seed~~ inspiration to the computer.\nThe Image class is only used to save and load files, no need to require it otherwise.\nSpriter works on a binary grid, which can be printed to a terminal.\n\n```Ruby\nrequire_relative 'Spriter'\n\n100.times {|inspiration|\n  srand(inspiration)\n  # 32x32 is my style, bigger images are harder to generate cool effects\n  spt = Spriter.new(32, 32)\n  # Generate a sprite with:\n  # 87% chance of replicating the left on the right\n  # 13% chance of replicating the top on the bottom\n  # You can also control the distribution function, probabilities and cleaning/adding loose pixels\n  spt.generate(87, 13)\n  # If you want to see it in the Terminal, print it\n  # just make sure the character \"\\0\" is invisible and \"\\1\" is visible\n  puts spt.to_s, '-' * 32\n  # Save it to a file, destination folder sprites/bmp/ must already exist\n  # You can also give foreground and background colors, default is green on black\n  spt.save(\"sprites/bmp/sprite_#{inspiration}.bmp\", 0,255,0,255, 0,0,0,255)\n}\n```\n\n## ToDo's\n- API documentation\n- More tests (Spriter, BMP and PNG with different image proportions)\n- Optional color based on neighbor count (may take a lot of CPU)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaumagnaguagno%2Fspriter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaumagnaguagno%2Fspriter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaumagnaguagno%2Fspriter/lists"}