Amateur Topologist

Politics, programming, math, and science.

Of Relativity, Balloons, and Automobiles

Suppose you’re driving a car on a perfectly level road at a perfectly constant velocity (because in physics problems, everything can be done perfectly unless the error is part of the problem). And suppose someone left a helium balloon inside the car, which is free to float around. Similarly, suppose someone left a ball resting on the floor of the car; since there are no jolts or other sudden accelerations, the ball is resting perfectly still. Now suppose you start accelerating forward. What happens to the ball and the balloon? Our physical intuition tells us that the ball will roll to the back of the car, because that’s what happens when we’re in an accelerating vehicle; things get pressed back. But what does the balloon do?

A lot of people, when presented with this, intuitively think that the balloon will move towards the back, just like the ball. If they’re physics-minded, they might say that the inertia of the balloon keeps it from wanting to change its velocity unless it’s forced to, and the only thing that can force it to do so is the back of the car. But if this were true, then general relativity would be wrong! Because according to general relativity, if you don’t look outside the car window or cheat in some other way, there’s no way to tell between a uniform acceleration of 9.8 meters per second per second in the forward direction and an additional gravitational force of one G pulling you back. And when we think about it in terms of gravity, the answer becomes clear: balloons in air will move against gravitational fields, so it will move to the front of the car.

Hopefully, this has left you at least somewhat unsatisfied; the question of where the balloon moves has been answered, but not why it moves. For that question, consider why balloons move against gravitational fields. They move because of buoyancy, which is a result of the fact that pressure is greater when you go ‘deeper’ into a fluid (liquid or gas) in a gravitational field. So the upwards force as a result of pressure is greater than the downwards force, and the balloon rises until something stops it or the atmosphere gets so rarefied that it reaches zero buoyancy (or, more likely, the decreased atmospheric pressure causes it to pop!) But when you have an accelerating car, the air molecules, much like the rolling ball, will tend to ‘pile up’ in the back, causing greater pressure in the rear than in the front. And in general, this pressure gradient will be enough to cause a forward force on the balloon all the way to the front of the car.

What to do when you lose your computer

I was recently thinking about security as a result of finding the hard copy of a PGP revocation certificate I had printed, when I realized: I had no clue what I should do in case the physical security of my computer was compromised (i.e., if it was stolen or went missing for an extended period of time). So I decided to take stock of how many secrets I have here and what the best way to render them useless or remote-erase them would be in case I lost it, as well as to make it hard for anybody who steals it to get any use out of the secrets before I can make them useless.

I store my passwords in a KeePass password database, encrypted using AES using a reasonably long passphrase; I have it set to require the passphrase if the window loses focus for more than 30 seconds. I then synchronize it using Dropbox between various computers, as well as so I can download it if I’m on a new computer that I trust enough to log into stuff on. I also have my PGP private key on my laptop. Noticeably, there are several password I have that are not written down anywhere: my MIT Kerberos password, my Gmail password (most of my password reset e-mails would be sent there, so if it was compromised everything else would be too), the KeePass database password, and my Dropbox password. And of course there’s a password on my laptop, but I wouldn’t rely on that for anything beyond keeping someone from looking at my stuff while I’m temporarily out.

So what does that mean in case the laptop’s stolen? Step one is to go everywhere I know of that I can sign into using public key cryptography on this machine and delete the keys; fortunately, the only such machine at the moment is the Github remotes and my VM; I’d also force-disconnect any ssh sessions that I left open by killing the processes. After that’s done, I change my Gmail password in case I left myself logged in or cookied or something, and forcibly sign out all my other accounts using the link on the bottom. If the password’s been changed or it’s been more than a day or so since I last saw the laptop, I assume all my accounts are compromised. The third step is to backup the password database and then delete it from Dropbox; if the person who stole it isn’t smart, the next time it connects to the internet it’ll delete the local copy. Interestingly, even if I change the Dropbox password, the computer will still have access to my files; if I want to disable syncing, I have to unlink the computer it on the website (which does tell me when the last sync occurred). Then I change the password for my AOL and MSN accounts; MSN only lets you sign on from one place at a time, but AOL doesn’t, and I don’t know if it’s possible to force a logout. Finally, I’d probably revoke my public key; I know enough people who can get it signed that are in the strong set that it’s not terribly difficult to get back in there. I have a printout of my revocation certificate for just this occasion.

Perl will never go away, ever

Perl was one of the first languages that I ever learned and actually truly did things with; it was the first language I ever wrote a nontrivial program in (a DES implementation that I have unfortunately lost the source code to, or else I would post it). The first language I ever wrote a program in was something I don’t even remember in BASIC; I seem to have blocked all memory of it from my memory, probably for the better. So I have a bit of a soft spot for the language, and so I still have some of my bad habits; since I didn’t use strict or -w, my code would likely be full of uninitialized variables and barewords. It’s a bad habit, and to this day I still have to be reminded occasionally that other languages, such as Python, do require variables to be declared.

But Perl is old now, and I’ve mostly moved on to other languages, like Python. I like the object-orientation, the support for functional paradigms and other nice things like list comprehensions and lambda functions. I like not having to sigil all of my variables with $ or @ or %, I like being able to supply keyword arguments to my functions so that I don’t have to remember which weird order I decided to use, I like the sheer amount of fun things that you can do with object orientation combined with reflection, metaprogramming, and everything being a first-class object. And yet, I still think it’ll stick around for a while.

Why do I say that? Simple. I was talking with someone who had left in the middle of an online IRC-based role-playing game, and they had asked for chatlogs of what had happened after they left. I had them, since I run weechat in tmux (like irssi in screen, but better!) and so am in every IRC channel I’m in 24/7. But the question was: how could I pull out just the lines that were said when he left? And the answer was Perl. It turns out that the .. operator, which in a for loop or other situations where a list is expected produces a range (so (1..9) as a list produces the list (1,2,3,4,5,6,7,8,9)), does something completely different in a scalar context, like in the conditional of an if statement. Take the statement print if (/Person.*has quit/ .. /Person.*has joined/). Each time this statement is run, the conditional will evaluate to false, until the left-hand side evaluates to true. Then it’ll start evaluating to true, until the right-hand side evaluates to false, and then it’ll stop being true (but it’ll still be true until it’s evaluated again!), etc., etc. So if this is in an implicit while loop running through the lines of a file, it’ll start printing when it sees a line saying Person has quit, including that line, then stop when they rejoin, but still print that line, and then it’ll keep going until it sees another quit line, etc. And the best part is, if you call perl with -n, you automatically get a while loop that assigns the current line of the file it’s reading from to $_, the implicit variable in the matching and print.

If I wanted to do that in something like Python, I’d have to manually set up the read loop, write a function to trawl through, build up regexp objects to match on, etc. And that’s fine for a piece of code I intend to maintain. But for a quick one-line script like this? Too much effort. All I need is perl -ne.

Making Terminal.app suck less

For various reasons (mostly due to the fact that iTerm would regularly spike in CPU usage whenever I was causing it to redraw large portions of the screen, causing noticeable lag), I’ve been forced to switch away from iTerm as my terminal emulator of choice, back to the standard Terminal.app. It’s a decent piece of software, but it has a few issues of its own.

First, it’s 16-color only, and there’s no way to change the colors. While the former can’t be helped, the latter can: if you install SIMBL, then download TerminalColors (either the Leopard version or the Snow Leopard version, whichever is appropriate), then unzip and move the resulting directory into /Library/Application Support/SIMBL/Plugins, then restart, you’ll be able to modify colors under Settings -> Text by clicking the new ‘More’ button.

Second, the Home and End keys, to put it bluntly, suck. They don’t work in most programs. The fix for this is to go to Settings -> Keyboard, then creating/editing the entry for the home key. Select ‘send string to shell’, and in the text entry field, press escape, then capital O, then capital H, so it reads \033OH. Then do the same for the end key, only this time insert \033OF. It should work pretty much everywhere. Page up is \033[5~  and page down is \033[6~.

There are a couple other things: add
alias grep="grep --color=auto"
alias rgrep="grep -r --color=auto"
alias grip="grep -i --color=auto"
alias rgrip="grep -ir --color=auto"
alias ls="ls -G"

to colorize ls and grep by default, as well as add some convenient aliases (grip for case-insensitivity, rgrep for recursive grep).

Security vulnerability in Haskell with CGI

Compiled Haskell programs all include special RTS (Run Time System) options, that change things like the number of cores that it runs on, various internal things relating to how often garbage collection runs, etc. They’re specified by invoking the program like ./foo +RTS -m10 -k2000 -RTS to run the GHC-compiled program ‘foo’, reserving 10% of the heap for allocation and setting each thread’s stack size to a maximum of 2000 bytes. In the current build of GHC, there is no way to disable these options from working (although the option –RTS will make all further options be interpreted as normal, non-RTS options). The problem is that the option -tout will write profiling data to the file out. So, if your program is setuid root, anybody who runs it can write the profiling data to, say /etc/passwd and render the system unusable. They don’t get to pick what gets written, so they can’t add a backdoor for themselves, but they can essentially scribble over whatever files they want. This is bug #3910, and the fix (disabling RTS by default) has been uploaded.

Now, one of the more little-known features of CGI is that if you pass a query string that does not contain any = signs to a CGI script, the httpd may pass the string along as command-line arguments. This is specified in section 4.4 of RFC 3875, and it specifies how the query string SHOULD be turned into arguments (although it does not say anything about whether the httpd should behave this way, only that some do). This is an example script that only outputs its arguments in a comma-separated list; the link gives it some sample arguments. Note that by URL-escaping, you can send arbitrary strings through… including +RTS. So if that were, say, a Haskell script, I could pass the query string ?%2BRTS+-tindex.html+-RTS and overwrite index.html.

There are three ways to get around this: first, GHC 6.12.2 has the -no-rtsopts option, which will obviously disable RTS options. So if you just recompile your script with that, it’ll be safe. Note that 6.14 will disable the RTS options by default; the 6.12.2 patch didn’t for backwards-compatibility reasons. Second, if you don’t want to use 6.12.2 for whatever reason, you can wrap it in a shell script that calls it with no options. For example, replace the Haskell script with a shell script called, say, hscript.cgi (if your Haskell program is called hscript) that calls it with no arguments, e.g.

#!/bin/bash
./hscript.real

and rename the Haskell script to hscript.real, so that it doesn’t get run as CGI (I’m assuming that .real files don’t get run as CGI on your machine!) Another thing you can do is to add the following to your .htaccess, which will give 403 Forbidden errors to anybody passing RTS arguments in the URL:

RewriteEngine on
RewriteCond %{QUERY_STRING} ^(?:[^=]*\+)?(?:%2[bB]|(?:-|%2[dD]){1,2})(?:%52|R)(?:%54|T)(?:%53|S)(?:\+[^=]*)?$
RewriteRule ^ - [F]

This will solve it for every Haskell script you use, but relies on the regex being correct, which isn’t something I can guarantee.

dissociated-blogosphere: never have to write an original post again!

For the past two weeks or so, I’ve been working off and on on a project called dissociated blogosphere (OSX and Linux binaries here). It takes a bunch of URLs, looks through them for an RSS for the raw content of the posts, and then stores the words of the posts in an array. It then picks N random, consecutive words (where in this case N is 2), and starts generating new text, by picking a new word x% of the time if x% of the time, the previous N words were followed by that word. For example, if 90% of the time, the words ‘the quick’ were followed by ‘brown’, and the other 10% of the time, they were followed by ‘red’, then when the two-word phrase ‘the quick’ was randomly generated, it would pick ‘brown’ 9 times out of 10, and ‘red’ 1 time out of 10. This is the algorithm Emacs‘s dissociated press feature uses, hence the name. Running it a few times on this site and picking some of my favorite sentences gives:

Second, I ignored the axes of the work you envision. So start small, and think about the free group on two generators, which is obviously highly undesirable behavior. However, it does have the web interface, I’ll have it up by last week, but that obviously didnt happen. Taking into account the fact that I’m using. The central thing that makes MS Paint Adventures unique to the point where it’s my go-to language for random programs (I still use Python for that), but if we pick two of them and rotate one of the set of all rotations that you have some custom function you want soup or salad, both is not a valid answer.

It’s my first medium-scale project written in Haskell (even though there isn’t a lot of code, what little was there was not trivial to write), and I’ve learned several lessons from it:

  • The Haskell wiki is an excellent resource. When I was trying to learn how to use HXT, the Haskell XML Toolbox, I found the provided documentation somewhat inadequate. But the HXT article on the Haskell wiki is an excellent introduction to the filter abstraction, which is all that I need for the basic stuff that I’m using.
  • Read the Haddock documentation. The HXT article, as useful as it was, didn’t cover a couple essential things I needed to know (such as how to pull all elements with type “application/rss+xml”). So I look at the documentation for Text.XML.HXT.Arrow.XmlArrow (the module containing the arrows that HXT uses to filter XML), and saw that hasAttrValue :: String -> (String -> Bool) -> a XmlTree XmlTree looks about right; from the type, I can guess (correctly) that I need to pass it the attribute and a prediate on the value of the attribute (i.e., hasAttrValue "href" (== "application/rss+xml")).
  • One goal at a time. This isn’t specific to Haskell. When I started on this, I meant for it to require you to provide the RSS feed. Then, I realized that having a larger corpus might be better, so I added the ability to pull from multiple feeds. Then I decided that expecting people to find the RSS feed by hand might be a bit much, so I rewrote it to pull the RSS feed from the site. And I eventually plan to write a CGI frontend so that you can just run it online. If I had decided from the start to do all these things, I probably never would’ve gotten started. As Linus Torvalds said:

    Nobody should start to undertake a large project. You start with a small trivial project, and you should never expect it to get large. If you do, you’ll just overdesign and generally think it is more important than it likely is at that stage. Or worse, you might be scared away by the sheer size of the work you envision. So start small, and think about the details. Don’t think about some big picture and fancy design. If it doesn’t solve some fairly immediate need, it’s almost certainly over-designed.

  • Strip and gzip your executables if you’re going to distribute them. Due to the fact that I’m statically linking in HXT, which is a sizeable library, the compiled, non-stripped version of dissociated-blogosphere is a whopping 12 megabytes. This isn’t due to inefficiencies in my own code, but due to the sheer size of the HXT library. Running the Unix command line utility strip (which only removes internal debugging information) cuts it down to about 5 MB, and then gzipping the binaries takes it down to a little over a megabyte.
  • Split things into libraries where it’s appropriate. Part of the problem with using HXT is that it makes recompilation slow; if I could do it all over again, I might have used HaXml, but HXT has the advantage of having nontrivial amounts of documentation written about it (on the Haskell wiki). If I had instead split the RSS parsing code into its own library, I could have only recompiled those parts whenever I touched them, which wasn’t nearly as often as I touched the code frontend. Plus, it’s just good programming practice.

So what do I have planned for dissociated-blogosphere? First off, I plan to make it faster by caching RSS lookups; by storing a map from page URLs to RSS feed, I can cut the number of network requests in half. Second, I plan to implement actual error handling; right now if you give it a bad URL it fails and doesn’t produce any useful output, regardless of whether other URLs are good. Third, I’m going to split out the RSS part into its own library, which I might make its own package on hackage. Fourth, I intend to eventually write a web interface (either in Haskell or in Python) so that you don’t have to download and install it. I originally intended to have the web interface up by last week, but that obviously didn’t happen. Taking into account the fact that it’ll take longer than I think it does, I’m guessing I’ll have it up by two weeks from now (so, a month). And finally, when/if I do the web interface, I’ll have it color the text according to which blog it’s from, or maybe even output xterm color codes if I don’t write the web interface.

What comes after reCAPTCHA?

reCAPTCHA, the system I use to keep spam out of the comments, is probably one of the most popular CAPTCHAs (Completely Automated Method[s] to Tell Computers and Humans Apart) out there. And for a very good reason: it draws its source words only from texts that current optical character recognition (OCR) technology is unable to read; therefore, no spam bot should be able to read them, especially after reCAPTCHA applies some extra distortion to render it absolutely non-machine-readable. But what do we do when the state of OCR technology advances to the point where they get as good as humans at reading text? As technology for reading words improves, it seems likely that within the next decade or two, the level of distortion necessary to render it unreadable by machines will also make it illegible to humans. So what next?

One class is image-recognition CAPTCHA: you present the user with ten distorted images (to prevent random guessing by bots) and ask them which ones contain a cat, or which ones have been rotated upside-down, or which ones are people. This is essentially a generalization of text-based CAPTCHAS, but it has several problems. First and foremost, you need a large source of images to show the user. This is one of the huge advantages of text-based CAPTCHAs: they can be procedurally generated. If the image database for a CAPTCHA service is small, then it’ll be passed around by spam bots; since recognizing whether two images are the same is a fairly solved problem, all they have to do is answer your question for each of the images once. (The distortion’s purpose is to make image comparison harder in case spammers do get a hold of your database, not to make it impossible). One method would be to browse Flickr for photos tagged with an object and assume that each such photo contains an object, but you’re running into copyright issues as well as essentially relying on the fact that someone won’t tag a photo ‘cat’ just because it has a kitten in the distant background.

One other idea that I’ve seen a couple sites use is knowledge-based, relying on the fact that machines can’t yet parse natural language. So it asks a question like “what is 2 plus 2?”. The fundamental problem I see with this is that, again, you’re going to have a very small repertoire of questions; a CAPTCHA has to be able to be generated by a computer. Not to mention the fact that whatever question-generating algorithm you use could just be reverse-engineered to extract content, then passed to Google or Wolfram Alpha to get the answer. Unlike images, there’s no way to ‘distort’ a question.

A third possibility, orthogonal to trying to tell real people from computers, is to look at the content of the message, rather than require the message sender to pass some arbitrary test. This is the approach Akismet (which comes by default on WordPress) uses, and is similar to the way e-mail clients detect spam. This has the downside of having a higher false positive rate than CAPTCHA-based methods. A short comment saying ‘Hey, I read your article and liked it; check out this link’ can either be legitimate or spam, and determining which one it is would require knowing the contents of the link. So your CAPTCHA system would have to visit links posted by users, which is obviously highly undesirable behavior. However, it does have the advantage of not relying on some problem being ‘hard’ to solve, and it also removes the (admittedly small) barrier to commenting that CAPTCHAs produce.

For now, reCAPTCHA will remain good enough; it’s easy to solve, and the word combinations that I can’t easily read can be dismissed with a click of the refresh button. And since I have very low traffic, I can afford to have an e-mail sent to me for every comment I get here; if it does wind up being spam (apparently, either reCAPTCHA isn’t completely impervious to computer solving or there’s some sweatshop worker whose job is to spam sites with cheap Viagra ads) I can just delete it.

I heard you like spheres: the Banach-Tarski paradox

In most people’s minds, or at least in the minds of those with a mathematical bent, every set of points S in \mathbb{R}^3 (a fancy way of writing three-dimensional Euclidean space) has a volume. Some sets, such as any set consisting of a finite number of points, has zero volume, whereas other sets, such as the set of all points less than one unit away from the origin, have finite but non-zero volume, and still other sets, such as \mathbb{R}^3 itself, have infinite volume. But does every set necessarily have a volume? It turns out the answer is no, even in one-dimensional Euclidean space; I showed this in a previous post. Instead, I’ll show you the Banach-Tarski paradox, which states that it is possible, via translations and rotations, to dissect a three-dimensional sphere (technically a 3-ball) and rearrange it into two spheres of the same radius as the original.

For simplicity, we’ll start by only looking at the surface of the sphere, S^2. Let x refer to rotation by \pi ^ \circ (that is, pi degrees) about the x-axis and let y refer to rotation by \pi ^ \circ about the y-axis. The exact degree of the rotation isn’t important, only that it should be impossible via any combination of x and y to go back to the origin. Let H be the set of all rotations that you can get by combining x, y, and their inverses. Then x and y form what’s known as the ”free group on two generators“, which is composed of all strings using x, y, x^{-1}, and y^{-1} as symbols, with the provision that x and x^{-1} cannot appear together for x=a,b. The multiplication for this group is just writing the strings together, subject to the rule that x and x^{-1} cancel, as do y^{-1} and y. So, for example, (xy^{-1}xxy^{-1})(yx^{-1}y)=xy^{-1}xxy^{-1}yx^{-1}y=xy^{-1}xy. The name arises from the fact that there are two ‘fundamental’ symbols, x and y, and it is ‘free’, since they don’t commute (that is, xy \neq yx).

Now, one very weird fact about the free group on two generators (written F_2) is that you can break it into four pieces, rearrange those four pieces, and then reassemble them into two copies of it. To elaborate, let S(x) denote the set of strings in F_2 that start with x, and similarly for the other three symbols; let e denote the empty string. Then obviously

F_2=\{e\}\cup S(x) \cup S(x^{-1}) \cup S(y) \cup S(y^{-1})
. But we also have (if we include e in aS(a^{-1}))
F_2=aS(a^{-1})\cup S(a)
The reason for this is that if the string s doesn’t start with a, then it’s the same string as aa^{-1}s and so is in aS(a^{-1}); otherwise, it’s in S(a). We also have, for the same reason,
F_2=bS(b^{-1})\cup S(b)

How is that relevant? Well, H partitions S^2 into orbits, where an orbit is a collection of points such that each element of H moves points from the orbit into the orbit, and points not in the orbit into another point not in the orbit. For each orbit, we can pick a point p inside it; let the set of all these points be P. Then we can turn the decomposition of F_2 into a decomposition of H; using H(x) to indicate the set of all rotations in H that start with x, we have

S^2=xH(x^{-1})\cup H(x)=H(x)\cup H(y) \cup H(x^{-1}) \cup H(x^{-1})

These four sets together make up the sphere (except for P itself; we’ll get back to that), but if we pick two of them and rotate one of those two, we also get back the sphere. Therefore, we can break down the sphere into four pieces and recompose them into two spheres. If we then draw lines joining the sphere to the origin, we can turn this into a decomposition of two solid spheres.

Now, there are two omissions in this proof: first off, I ignored the origin. It turns out that you can cut the sphere without the origin up, rearrange it, and then put it back together so that you have the origin. Second, I ignored the axes of the rotations in H; they are the fixed points of these rotations, and so might cause problems; it turns out that if we call those points D; we can again cut up S^2-D and rearrange it into S^2.

So what does this mean? If we want to have any reasonable definition of volume, then we want translations and rotations to leave it the same. Then, we must have that some of these sets don’t have a well-defined volume; much like how you can construct a subset of the real line that doesn’t have one. We break the sphere down into a number of sets, some of which don’t have a defined volume; then we move them around and reassemble them. There’s no contradiction here, since two sets that don’t have a well-defined volume (or non-measurable sets, as they’re known) might have a union that has a volume. Of course, this is physically meaningless, since you can’t actually perform the required divisions; atoms are not infinitely small, after all.

MS Paint Adventures: one of the first true webcomics

This blog post contains minor spoilers for Homestuck.

The phenomenon of the webcomic is not exactly new by any means; Sluggy Freelance, one of the oldest still-running webcomics, is 12 years old, only slightly older than widespread availability of the Internet. So what do I mean when I say that MS Paint Adventures is one of the first first few webcomics? It’s one of the few (that I’m aware of, of course) that actually uses the full potential of a comic that takes place online.

The central thing that makes MS Paint Adventures unique to the best of my knowledge among comics is that the story is in large part driven by the fans. Andrew Hussie, the author, has stated in an interview that he does not plan out the direction the plot will take in advance; although he has some overall ideas of the direction he wants it to go, he lets the reader suggestions dictate it to a larger extent than essentially any other webcomic, or indeed any other form of serial storytelling. The only reason that this is possible is because of the MSPA forum; while it would be possible for this to happen in a world without the Internet, an online forum allows for other users to voice their approval for options that they might otherwise not have thought of, giving Hussie the ability to judge what the readers want. But even elements of discussion that are not necessarily suggestions can generate plot points; the apocalyptic nature of the story was originally unplanned and came about at least partly as a result of discussion on the forums about the posters on the walls of one of the main characters for apocalypse-themed movies.

The various Flash animations and other non-static content that AH occasionally uses to enhance the story are another element of MSPA that fundamentally would not work in a traditional print comic. Although they do not necessarily advance the story any better than a series of corresponding still images would, they make the story more enjoyable and immersive; the end-act flashes are probably the quintessential example of this, reminding the reader of the various active story threads, while providing a bit of progression in each of them; it’s far more effective than a series of still panels could ever hope to be. And the Flash animation/game not only could not be executed without the internet, it positively requires the high-bandwidth connections of today’s modern Internet infrastructure. The soundtracks to the Flash animations are also a key part of the overall ‘experience’, even though Andrew Hussie himself does not compose them; the collaboration between artist and composer is only possible through the Internet.

Although MSPA is certainly not the first comic to use the internet, or even the first one to use it beyond as a medium for publishing images (other comics have certainly had associated discussion forums), I believe that it’s the first one to truly use the full potential of the Internet. And while that doesn’t necessarily make it better than other ones, it definitely moves it from the realm of good to that of great.

Note that I have said that it is one of the few true webcomics, not the only one. The only other one that comes to mind is Kid Radd, which makes extensive use of animation, especially in the later strips, where almost every ‘panel’ is a three-second animation. But it didn’t use Flash, or user suggestions, so as good as it is, I don’t consider it to use the Internet in the same way that MS Paint Adventures does.

The end of Tonal Tuesday

Tonal Tuesday was something that I decided to do when I was going through a creativity drought as a means of making sure that something would always get posted weekly in hopes of inspiring me; although it seems to have failed that, I am getting back into the swing of finding things to write about, so I’m not going to be doing it anymore. Plus it’s a little embarrassing seeing it being the largest category of posts on the site.