That's how it works, yeah. On each turn, I scan an area, come across a yellow or blue pixel, then feed its position to a function processing whatever rules apply to it. The only proper optimization I have right now is that dirty rect I mentioned: as processed pixels make new records in distance from spawn, they push the boundaries of the scanned area. But it only really matters in the early stages, as the screen gets filled you could do something based on large uniform areas.
Oh, and the order of looping matters. Most importantly, it won't work as expected if you go up-to-down instead of the other way around, because falling grains would overwrite the ones underneath. Also, you can see how grains ride slopes differently on the left and right sides of a pile (it looks like it's winding left inside the cavern, because I'm going left-to-right). It's a problem inherent to systems that change while being scanned.
I wrote
quicksand, one of the less significant falling sand games. it was still fully-featured, apart from it being my very first real program and so lacking a lot of the good coding practices that the others certainly had it really did live up to wx and burning sand.
I was going to give you the source code but I'm having trouble finding it. I think I remember how I did most things though so I could hand out advice and stuff.
most notably: once you have elements that fall up (like fire, for example) that solution of scanning bottom-to-top won't work anymore. keeping a second array with the new positions and then switching to it only after the update is probably the best solution.