<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Luke Hatcher]]></title><description><![CDATA[Because one of these decades I'll decide to write again]]></description><link>https://luke.io/</link><image><url>https://luke.io/favicon.png</url><title>Luke Hatcher</title><link>https://luke.io/</link></image><generator>Ghost 5.101</generator><lastBuildDate>Mon, 08 Jun 2026 23:29:09 GMT</lastBuildDate><atom:link href="https://luke.io/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Restoring Old Posts]]></title><description><![CDATA[<p>I&apos;m in the process of restoring a few of my blog posts from a decade ago. These are all largely out of date and the formatting won&apos;t be perfect, but as part of creating a new comfy home here I&apos;d like to preserve some</p>]]></description><link>https://luke.io/restoring-old-posts/</link><guid isPermaLink="false">5cfb5f17f0a3f351b4f7ec20</guid><dc:creator><![CDATA[Luke Hatcher]]></dc:creator><pubDate>Sat, 08 Jun 2019 07:12:57 GMT</pubDate><content:encoded><![CDATA[<p>I&apos;m in the process of restoring a few of my blog posts from a decade ago. These are all largely out of date and the formatting won&apos;t be perfect, but as part of creating a new comfy home here I&apos;d like to preserve some of my older content.</p>]]></content:encoded></item><item><title><![CDATA[Store Binary Data in Twitter with Tootfiles]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>Sometimes a man&apos;s worst ideas lead to his finest moments: Ben Franklin decides to fly a kite in a storm and we get electricity (I&apos;m paraphrasing that story), Alexander Graham Bell shacks up with his 15-year-old apprentice (who was deaf) and we get the telephone (paraphrasing</p>]]></description><link>https://luke.io/store-binary-data-in-twitter-with-tootfiles/</link><guid isPermaLink="false">5cfb6564f0a3f351b4f7ec46</guid><dc:creator><![CDATA[Luke Hatcher]]></dc:creator><pubDate>Tue, 12 May 2009 12:00:00 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>Sometimes a man&apos;s worst ideas lead to his finest moments: Ben Franklin decides to fly a kite in a storm and we get electricity (I&apos;m paraphrasing that story), Alexander Graham Bell shacks up with his 15-year-old apprentice (who was deaf) and we get the telephone (paraphrasing again).</p>
<p>Now, I don&apos;t fly kites and my wife is neither 15 years old nor deaf, but I feel you and I might be on the cusp of solving the world&apos;s data storage needs as I, too, have a terrible idea: <strong>let&apos;s put <em>everything</em> in Twitter.</strong> <em>Everything.</em> Music, photos, tax forms--you name it, we should be able to store the data in a Twitter stream.</p>
<h3 id="brilliancebegetstootfiles">Brilliance begets &apos;tootfiles&apos;</h3>
<p>So yes, you probably have realized that storing all of your binary data in Twitter is one of the top five or six ideas of this decade, so you&apos;re as eager as I am to begin implementation. Luckily, I&apos;ve already gone to the trouble and written a Python script that does just this: I call it <a href="http://github.com/lukeman/tootfiles/?ref=luke.io">tootfiles</a>.</p>
<p><strong>Note: What follows is a detailed look at how this project was built and the various choices I made along the way. If the details of character encodings and data compression bore you and you&apos;re only interested in seeing the final code or using the script yourself, you can check out the <a href="http://github.com/lukeman/tootfiles/?ref=luke.io">tootfiles project page over at my Github account</a>. I promise I won&apos;t judge you.</strong></p>
<h3 id="aquickandfaultylookatthenumbers">A Quick (and faulty) Look at the Numbers</h3>
<p>Twitter allows us to post individual text messages to its service with up to 140 bytes per message. Assuming for a moment that each binary byte can be represented as one character byte in a toot, it is fairly simple to gauge how many toots it will take to represent a file given its size in bytes. For example, the <a href="http://www.google.com/intl/en_ALL/images/logo.gif?ref=luke.io">standard sized Google search logo</a> weighs in at 8,558 bytes. Divided up into 140 byte segments, you might determine that we could represent this file with 62 individual toots. That sounds like a lot of bit-sized messaging, but consider that internet celebrity <a href="http://twitter.com/scobleizer?ref=luke.io">Robert Scoble</a> has over 20,000 toots to his name and still has over 90,000 followers fawning over whatever it is that he actually does.</p>
<p>A more amusing case study might be trying to store a music file in your Twitter stream. Doing a quick search for some legal music to test with, I&apos;ve found a punchy little number from the year 1909 called &quot;<a href="http://www.archive.org/details/JohnGoAndPutYourTrousersOn1909?ref=luke.io">John, Go and Put Your Trousers On</a>.&quot; With this short piece weighing in at 3,586,134 bytes, it would only take 25,615 individual toots to represent it as a tootfile. I&apos;ll admit that this is a disturbingly high number of messages to store a small music file, but life is about trudging through dead end projects and never letting it break your spirit, so we&apos;re just going to pretend it&apos;s not a problem. Did you hear that? <em>It&apos;s not a problem.</em></p>
<h3 id="realitybytes">Reality Bytes</h3>
<p>So the truth is we can&apos;t just slice the files up and stick the pieces into a toot. Binary files are a different beast from the short, nearly plaintext messages of Twitter. To fit any possible binary byte into a string that we can store in Twitter, we&apos;ll need to store these bytes using some type of standard encoding system.</p>
<h4 id="choosinganencodingsystem">Choosing an encoding system</h4>
<p>The two candidate encoding systems I evaluated for this project were Base64 and Base85, with the trailing number indicating the size of the usable character set. To understand this a little better, remember that binary is base 2 (it can represent any number using only 1 or 0) and the standard numerical representation is base 10 (representing any number using 0-9). You could theoretically encode anything using any base, but the higher the base the tighter you can pack the data in.</p>
<h4 id="sizematters">Size Matters...</h4>
<p>I said before that the higher the base, the more efficiently you can pack data into your string. To examine this a little closer, let us look at the real world difference between Base64 and Base85 encodings.</p>
<p>Base64 uses the character ranges <code>A&#x2013;Z</code>, <code>a&#x2013;z</code>, and <code>0&#x2013;9</code> for its first 62 character slots. Depending on the implementation (and there are a few), the remaining two characters differ. In mapping binary to this encoding scheme, Base64 uses four characters to represent three bytes of data. This leads to a roughly 33% increase in size when representing a binary file in Base64.</p>
<p>Base85 (as defined in <a href="http://www.rfc-editor.org/rfc/rfc1924.txt?ref=luke.io">RFC1924</a>) uses the character ranges <code>0&#x2013;9</code>, <code>A&#x2013;Z</code>, <code>a&#x2013;z</code>, and the 23 characters <code>!#$%&amp;()*+-;?@^_`{|}~</code> to represent data. By using five characters to represent four bytes of data, Base85 typically can represent a binary file with a 25% increase in size.</p>
<p>To compare these encodings using our two example files from above, I wrote a script that encodes the files with Base64 (as it&apos;s included in the standard lib) and used <a href="http://paste.lisp.org/display/72815?ref=luke.io">this pure Python implementation</a> which does the same with Base85. You can compare the file sizes in the chart below.</p>
<p><img src="http://media.lukehatcher.com/lh/posts/tootfiles/filechart.png" alt="Chart" loading="lazy"></p>
<p>There seem to be no surprises there, as the numbers back up our earlier claims with regard to encoding efficiency.</p>
<h4 id="butcharactersetsmattermore">... But Character Sets Matter More</h4>
<p>So yeah--Base85 is much more efficient; that&apos;s the pick, right? I&apos;ll now ask that you hold on for just one moment, as there is one more issue that we must consider: the characters used to represent our data in the encodings.</p>
<p>You see, Twitter has a little known &apos;feature&apos; that does a bit of post-processing to your messages to prevent bad guys from inserting scripts into a Twitter stream. This sanitization of Twitter messages takes certain special characters and converts them into HTML entities, meaning that characters like ``, and <code>&amp;</code> get expanded into safer representations of themselves.</p>
<p>Sanitizing messages is great because it keeps you and I safe when we&apos;re tooting away, but this has serious implications for our data storage scheme. You see, converting these single characters into HTML encodings means the characters now take up four bytes instead of one. Base85 includes a number of characters that Twitter translates into HTML entities, so our 140 byte messages might actually end up taking up much more than that and cause problems in the decode process.</p>
<p>For the above reason, its smarter for us to stick with good old Base64 encoding. Even without Base85&apos;s character issues (get it?), there&apos;s something to be said for having a standard Base64 implementation baked right in to most standard libraries as well.</p>
<h3 id="implementationdetails">Implementation Details</h3>
<p>Instead of walking through every line of the code, I want to just highlight some of the other choices I made and what it means for the finished product. This is broken down into two sections--one for posting to and one for decoding files from Twitter--as the situations require different approaches. The code snippets are shown as Python functions for brevity, but you&apos;ll notice that the <a href="http://github.com/lukeman/tootfiles/?ref=luke.io">final code</a> handles things with an object-oriented design.</p>
<h3 id="postingdatatotwitter">Posting Data to Twitter</h3>
<h4 id="compression">Compression</h4>
<p>Before encoding our data to be posted to Twitter, we should really employ some type of simple data compression. Using the standard <code>zlib</code> package that ships with Python, I ran our two example files from before through a quick script to see how much compression really helps us. The snippet below (shortened at the expense of <a href="http://www.python.org/dev/peps/pep-0008/?ref=luke.io">PEP 8</a>) should give you an indication of how to do this on your own.</p>
<pre class="brush: python; gutter: false;">
import os, sys, zlib, base64

f = open(&apos;somefile&apos;, &apos;rb&apos;)
fcompressed = zlib.compress(f.read())
fencoded = base64.b64encode(fcompressed) 
</pre>
<p>The results from the run are shown in the following chart.</p>
<p><img src="http://media.lukehatcher.com/lh/posts/tootfiles/filecompresschart.png" alt="Compressed file chart" loading="lazy"></p>
<p>This chart actually surprised me a bit, as I had only hoped to shrink the file down enough to save <em>some</em> of extra cruft we saw after encoding. While the compressed Google logo is only slightly smaller than the Base64 encoded file, our rousing hit song about trousers from 1909 has been reduced to a startling <em>82% of the original file&apos;s size</em>. Your mileage will vary on this, as some file formats compress better than others while many formats are compressed by default, but it&apos;s clearly a huge win to include a bit of fast compression to our code.</p>
<h4 id="getyourtootinthedoor">Get Your toot in the Door</h4>
<p>So now that we have a decent handle on our compressed and encoded data, it&apos;s time to think about getting it tooted. Before posting the messages to Twitter, we&apos;ll first need to split the large string of data into 140 byte chunks. The below function does just this, given a <code>data</code> string and defaulting the slice size to 140.</p>
<pre class="brush: python; gutter: false;">
def segment(data, n=140):
  &apos;&apos;&apos; Given the encoded string, slice it into twitter ready array elements &apos;&apos;&apos;
  tootcount = int(math.ceil(len(data)/float(n)))

  slices = range(tootcount)
  slices.reverse()
  
  return [data[i*n:(i+1)*n] for i in slices]
</pre>
<p>You&apos;ll notice that we reverse the range in the <code>segment()</code> function, as we want to build the list from tail to head. This reverse order makes reading the individual toots easy later on in the decode section, as we can simply iterate through the stream grabbing the toots in order.</p>
<p>Including some type of header helps signify the start of a file, along with a bit of metadata. Header information is appended to the list (to be posted last) using the following format:</p>
<pre class="brush: python; gutter: false;">
tootlist = segment(data)

# Header Information
header = &quot;|Tootfile:&apos;%s&apos; MD5:&apos;%s&apos; Count:&apos;%s&apos;|&quot; 
         % (filename, md5hash, tootcount)

tootlist.append(header) # Insert the header
</pre>
<p>With this header, we&apos;re helping future decode runs by including three important pieces of information: the name of the file, the MD5 hash of the file data (for integrity checking), and the number of data segments that will follow the header.</p>
<h4 id="publishingthesegments">Publishing the Segments</h4>
<p>Publishing the toots to Twitter is accomplished using the Twitter-python library. It&apos;s a fairly simple library to use (<a href="http://twitter.com/montylounge/status/1682434987?ref=luke.io">despite the uppercase method names</a>), so I won&apos;t get into the details of this other than to provide a quick look at the <code>publish()</code> method:</p>
<pre class="brush: python; gutter: false;">
def publish(data, username, password):
    api = twitter.Api(username, password)
    for toot in data:
        for retries in range(5):
            try:
                status = api.PostUpdate(toot)
                break
            except:
                if retries == 4:
                    raise Exception(&apos;Unable to post a segment. Quitting.&apos;)
                else:
                    time.sleep(1)
    print &quot;Finished.&quot;
</pre>
<p>There&apos;s some rudimentary fault tolerance built in, as I encountered some timeout issues when tooting a larger file. The run will fail if it cannot post a single toot with five attempts.</p>
<h3 id="retrievingdatafromtwitter">Retrieving Data from Twitter</h3>
<p>This part of the project was a bit tricky, as the Twitter API does not seem to provide a good way to access a single user&apos;s stream of toots for more than a single page. My preferred method was for a user to only need to know the ID of a tootfile header when retrieving files.</p>
<p>The first issue I ran into was not knowing the username to grab all of the toots from. I ended up using <a href="http://www.undefined.org/python/?ref=luke.io"><code>simplejson</code></a> and using a call to <code>http://twitter.com/statuses/show/&lt;tootid&gt;.json</code> to grab enough info given a single Twitter message ID to do the rest of our business. The key piece of info I needed was the Twitter username that owns the toot. I could have settled and required a full URL path to the header (which includes username), but this was a choice I made--right or wrong--as to how I wanted this implemented.</p>
<p>After reading a <a href="http://www.movingtofreedom.org/2009/03/18/python-script-for-backing-up-twitter-statuses/?ref=luke.io">blog post from Scott Carpenter</a>, I decided to just use the fantastic Python HTML scraping library <a href="http://www.crummy.com/software/BeautifulSoup/?ref=luke.io"><code>BeautifulSoup</code></a> to grab the tweets out of the given users stream. While Scott&apos;s goal was to archive all of his Twitter messages, I was able to simplify the script to grab just the info we needed. The modified section is below.</p>
<pre class="brush: python; gutter: false;">
def walk(username, headerid, tootcount):
    tootlist = []
    grabbedtoots = 0
    
    url = &apos;http://twitter.com/%s?page=%s&apos;
    re_status_id = re.compile(r&apos;.*/status/([0-9]*).*&apos;)
    
    # find the max number of pages, based on 20 per page
    maxpages = int(math.ceil(int(tootcount)/20.0))

    for page in range(1, maxpages+1):
        f = urllib.urlopen(url % (username, page))
        soup = BeautifulSoup(f.read())
        f.close()

        toots = soup.findAll(&apos;li&apos;, {&apos;class&apos;: re.compile(r&apos;.*\bstatus\b.*&apos;)})
        if len(toots) == 0:
            break

        for toot in toots:
            # Do we need more toots? If so, keep going
            if grabbedtoots &lt; int(tootcount):
                m = re_status_id.search(toot.find(&apos;a&apos;, &apos;entry-date&apos;)[&apos;href&apos;])
                status_id = m.groups()[0]

                # Look for the message directly after our header
                if(int(status_id) &lt; int(headerid)):
                    data = str(toot.find(&apos;span&apos;, &apos;entry-content&apos;).renderContents())
                    tootlist.append(data)
                    grabbedtoots += 1
            else:
                break

        # one second delay between pages 
        time.sleep(1)
</pre>
<p>There&apos;s nothing too fancy going on here--just some pulling down of the Twitter pages and scraping out the messages. This implementation of <code>walk()</code> is naive and assumes that you only have one tootfile in your stream and that it is located at the front of your stream. I consider this a bug, so it will be fixed eventually in the actual project.</p>
<p>Reassembling the data is fairly trivial once you have the data in a list. The following snippet works backwardly compared to the encryption, peeling back the layers of compression and encoding before leaving you with a string representing the file data.</p>
<pre class="brush: python; gutter: false;">
def decode(tootlist):
    &quot;&quot;&quot;Decodes the raw data given a list of toots&quot;&quot;&quot;
    data = &quot;&quot;.join(tootlist)
    compressed_data = base64.b64decode(data)
    rawdata = zlib.decompress(compressed_data)
    md5hash = md5.new(self.rawdata).hexdigest()
</pre>
<p>With that, we&apos;re done. The decode process can now check the md5 hash of the assembled data against what&apos;s in the header and write out the <code>rawdata</code> to either a file or standard out.</p>
<h3 id="grabthesourceandforkitforyourownneeds">Grab the source and fork it for your own needs</h3>
<p>If you&apos;re interested in the full (and slightly more proper) Python implementation of tootfiles, you can grab the source code and documentation over <a href="http://github.com/lukeman/tootfiles/?ref=luke.io">at its Github project</a>. The script can either be used as a standalone command line encoder, or you can use it as a Python library.</p>
<p>I&apos;m releasing the script with an MIT license, which basically means you can do whatever you want with it.</p>
<p>This is mostly a proof of concept release, as the whole idea of storing meaningful amounts of data in 140 byte segments is absolutely silly. Even as I write this, I&apos;m aware of a few bugs that would occur if someone were to try and use it heavily. With that said, I&apos;ll continue to iron things out and I will gladly accept any patches as well.</p>
<p>If you&apos;re interested in seeing some actual postings from the script, check out <a href="http://twitter.com/tootfiles?ref=luke.io">tootfiles</a> on Twitter. Have fun, and <em>please</em> don&apos;t spam your followers with a tootfile--especially if you&apos;re someone that I&apos;m following. ;)</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Coloring Sprites with Cocos2d-iPhone]]></title><description><![CDATA[<p>As someone who enjoys finding better ways to do things, I wanted to share a new tip I learned this weekend. Do you find yourself wrangling a handful of all too similar sprites, only varying by color? A useful pattern in game development is to keep the number of sprites</p>]]></description><link>https://luke.io/coloring-sprites-with-cocos2d-iphone/</link><guid isPermaLink="false">5cfb5b71f0a3f351b4f7ec0f</guid><dc:creator><![CDATA[Luke Hatcher]]></dc:creator><pubDate>Sun, 19 Apr 2009 12:00:00 GMT</pubDate><media:content url="https://luke.io/content/images/2019/06/mario-2.png" medium="image"/><content:encoded><![CDATA[<img src="https://luke.io/content/images/2019/06/mario-2.png" alt="Coloring Sprites with Cocos2d-iPhone"><p>As someone who enjoys finding better ways to do things, I wanted to share a new tip I learned this weekend. Do you find yourself wrangling a handful of all too similar sprites, only varying by color? A useful pattern in game development is to keep the number of sprites in your project to a minimum by performing what&apos;s called a <a href="http://www.giantbomb.com/palette-swap/92-1434/?ref=luke.io">palette swap</a> of the colors in the images. Fortunately for us, Ricardo Quesada&apos;s <a href="http://code.google.com/p/cocos2d-iphone/?ref=luke.io">cocos2d iPhone game engine</a> makes it a piece of cake to color your textures on the fly.</p><p>Credit for this idea goes to <a href="http://groups.google.com/group/cocos2d-iphone-discuss/browse_frm/thread/ca9ea0a9d02e95da?tvc=1&amp;q=setRGB&amp;ref=luke.io">Codemattic</a> from the <a href="http://groups.google.com/group/cocos2d-iphone-discuss/?ref=luke.io">cocos2d-iphone discussion group</a>, but I thought it was worth expanding upon to spread the knowledge and throw some code around while I&apos;m at it.</p><h3 id="let-s-gather-our-tools">Let&apos;s gather our tools</h3><p><em>Note: Code snippets in this article assume that you&apos;re using version 0.7.1 or later of cocos2d. If you&apos;re interested in grabbing the final product, you can <a href="http://cloud.github.com/downloads/lukeman/cocos2d-sprite-color-demo/cocos2d-sprite-color-demo.zip?ref=luke.io">download the finished project</a> from GitHub. You can also browse the source tree or fork it for yourself at the <a href="http://github.com/lukeman/cocos2d-sprite-color-demo/?ref=luke.io">GitHub project page</a>.</em></p><p>To implement this demo, <strong>you will need to have a working cocos2d project to hack with</strong>. My code snippets will live in the SimpleGame project that I co-maintain over at GitHub. If you&apos;d like to play along, <a href="http://cloud.github.com/downloads/monoclestudios/simplegame/simplegame.zip?ref=luke.io">grab a copy of SimpleGame</a> and have it handy.</p><figure class="kg-card kg-image-card"><img src="http://media.lukehatcher.com/lh/posts/sprites/colorsprite.png" class="kg-image" alt="Coloring Sprites with Cocos2d-iPhone" loading="lazy" title="ColorSprite"></figure><p>Also, grab a copy of the simple humanoid sprite to the right--we&apos;ll be adding it to our project to demonstrate the effect. You can either <a href="http://media.lukehatcher.com/lh/posts/sprites/colorsprite.png?ref=luke.io">right-click on this link</a> or save the image using your browser.</p><h3 id="implementation">Implementation</h3><h4 id="import-your-sprite-and-draw-it">Import your sprite and draw it</h4><p>The first thing you&apos;ll want to do is open up the XCode project you&apos;ll be working with. Feel free to use your own cocos2d 0.7.1+ project, or use the <a href="http://cloud.github.com/downloads/monoclestudios/simplegame/simplegame.zip?ref=luke.io">provided one from above</a>. From this point on, I&apos;m going to assume you&apos;re using the provided project. If you have your own, I expect you&apos;ll know more about where the code should go than I do.</p><p>Once you&apos;re in XCode, right click on <code>Resources</code> in the project pane to the left and choose <code>Add -&gt; Existing Files...</code> from the context menu. Browse to find your copy of the humanoid sprite from above and click <code>Add</code>. On the next dialog,<br>click the checkbox next to <code>Copy items into destination group&apos;s folder (if needed)</code> and then choose <code>Add</code> again.</p><p>Now that the sprite image is added to your project, let&apos;s write some code to display it on the screen. We&apos;ll be working in the <code>GameLayer</code> portion of the <code>GameScene</code>, so open up <code>GameScene.m</code> and find the <code>init</code> method on or around line 27.</p><p>Modify <code>init</code> to look like this, changing &quot;ColorSprite.png&quot; to whatever name you might have saved the image as:</p><!--kg-card-begin: markdown--><pre><code class="language-objective-c">- (id) init {
    self = [super init];
    if (self != nil) {
        isTouchEnabled = YES;
        
        // Add our original sprite to the layer
        Sprite *plainSprite = [Sprite spriteWithFile:@&quot;ColorSprite.png&quot;];
        [plainSprite setPosition:cpv(150, 160)];
        [self addChild:plainSprite z:0];
    }
    return self;
}
</code></pre>
<!--kg-card-end: markdown--><p>Assuming no typos, and that you&apos;ve referenced the sprite filename correctly, hit <code>Build and Go</code> in the XCode toolbar to run the project. Once the app is running, choose the <code>Start Game</code> option on the menu and you&apos;ll something like what I have below. (I took the liberty of changing the background of the layer to make the screenshots clearer)</p><figure class="kg-card kg-image-card"><img src="http://media.lukehatcher.com/lh/posts/sprites/plainsprite.png" class="kg-image" alt="Coloring Sprites with Cocos2d-iPhone" loading="lazy"></figure><h4 id="color-away">Color away</h4><p>We&apos;re in a good spot now to see the power of coloring our sprite. To accomplish this, we&apos;ll add another copy of our sprite to the layer and employ the <code>setRGB:::</code> method to it.</p><p>Add the following block right below the three lines you added previously to the <code>init</code> method.</p><!--kg-card-begin: markdown--><pre><code class="language-objective-c">        // Add a red sprite onto the layer
        Sprite *redSprite = [Sprite spriteWithFile:@&quot;ColorSprite.png&quot;];
        [redSprite setPosition:cpv(200, 160)];
        [redSprite setRGB:255 :0 :0];
        [self addChild:redSprite z:0];
</code></pre>
<!--kg-card-end: markdown--><p>Notice the only difference in the above code is our use of the setRGB method on the sprite. Using standard RGB color values, you can theoretically tint your sprite into 16,581,375 colors. For this demo, we&apos;re sticking to the primary colors.</p><p>Also, <em>and this is important</em>, the math behind this method is ruthless--it works nicely with a white (RGB 0,0,0) pixel, but might do scary things to your orange boot sprite. I trust that you will use this tastefully.</p><p>Before you run the above code, let&apos;s add two more sprites right below it to really spruce things up.</p><!--kg-card-begin: markdown--><pre><code class="language-objective-c">        // Add a green sprite onto the layer
        Sprite *greenSprite = [Sprite spriteWithFile:@&quot;ColorSprite.png&quot;];
        [greenSprite setPosition:cpv(250, 160)];
        [greenSprite setRGB:0 :255 :0];
        [self addChild:greenSprite z:0];
        
        // Add a blue sprite onto the layer
        Sprite *blueSprite = [Sprite spriteWithFile:@&quot;ColorSprite.png&quot;];
        [blueSprite setPosition:cpv(300, 160)];
        [blueSprite setRGB:0 :0 :255];
        [self addChild:blueSprite z:0];
</code></pre>
<!--kg-card-end: markdown--><p>These two blocks will draw a green and blue copy of our sprite. If you&apos;ve typed everything correctly, you can run the project now and see the brightly colored fruits of our labor.</p><figure class="kg-card kg-image-card"><img src="http://media.lukehatcher.com/lh/posts/sprites/finished.png" class="kg-image" alt="Coloring Sprites with Cocos2d-iPhone" loading="lazy"></figure><h3 id="conclusion">Conclusion</h3><p>I hope you find this trick as useful as I have. While we&apos;ve covered a simple example where the entire sprite is tinted to one color, one could (and should) use this approach to create complex, layered sprites with interchangeable parts.</p><p>Trouble? Let me know in the comments, but feel free to <a href="http://cloud.github.com/downloads/lukeman/cocos2d-sprite-color-demo/cocos2d-sprite-color-demo.zip?ref=luke.io">grab a copy of the finished project</a> if you&apos;re interested.</p><p><strong>Update 4/29/2009:</strong> <a href="http://code.google.com/p/cocos2d-iphone?ref=luke.io">Cocos2d-iphone v0.7.2</a> has been released which includes two new actions, TintBy and TintTo, which might be of interest to readers of this post.</p>]]></content:encoded></item></channel></rss>