All posts by Eevee

Goodbye, Twigs

Post Syndicated from Eevee original https://eev.ee/blog/2019/10/26/goodbye-twigs/

Twigs lounging in a cat tree, while a bright sunbeam illuminates him from behind

I did not expect my return to writing to be like this.

Twigs, our nine-year-old sphynx cat, has died.

He is survived by Pearl, his lovely niece; Anise, his best friend and sparring partner; Cheeseball, his wrestling protégé; and Napoleon, his oldest and dearest friend.

Twigs was Ash’s¹ cat, more than I have ever known anyone to be anyone’s cat. He loved them so much. No matter where in the house they went to sit or lie down, Twigs was practically guaranteed to appear a short time later to insert himself into their lap.

¹ For those who’ve been following along for some time, Ash used to go by Mel.

If there was no room for him, or Ash rebuffed him for whatever reason, or if he was just in the mood, his backup plan was to sit somewhere else and keep an eye on them. Sometimes I’d be talking to Ash and catch sight of Twigs behind them, staring at them. Just watching. I’d tell Ash, and they’d turn around and giggle at him, and he’d keep on staring. Sometimes they played hide-and-seek with him, ducking out of sight and then peeking back out at him; he might still be staring, or he might have trotted over to see where they went. Or they could call out to him, just say his name, and he’d acknowledge them with a little meow and come over. They could summon him silently, too, with nothing more than eye contact and a particular nod.

Sometimes we’d be sitting apart and Twigs would sit on me instead, laying chest-to-chest against me. He’d play this ridiculous game where he’d nuzzle my chin a few times, then look at Ash for a moment before doing it again. As if to say, hey, look what you’re missing out on. Or maybe just to say he hadn’t forgotten about them.

Twigs liked to sit at the top of the cat tree in our dining room, right in the path of a huge sunbeam for much of the day, where he could watch Ash at their desk and also see most of the house. We got a huge beanbag over the summer and put it behind Ash’s desk, and Twigs spent a lot of time there as well. He did his own thing at times, certainly, but it was rare for a day to go by without Twigs trying to be close to Ash.

If Ash was inaccessible — in someone else’s bedroom with the door closed, or in the backyard, or even in the bathroom for too long — Twigs would sit at the objectionable door and yell for them. I can’t think of many other cat meow I’d describe as a yell, but that’s definitively what Twigs did. MYAOOOW? MYEHHHH! When Ash was out of town, I’d often hear him trotting up and down the upstairs hallway, yelling for them — until he gave up looking for the moment and came to snuggle with me, just as intensely, like I were the one he’d been looking for all along.

His favorite thing in the world was bedtime, when Ash would finally not be distracted by anything else, and he could lay with them all night. All the cats sleep with us to varying degrees, but Twigs was usually the first to show up. His arrival was so distinct: the quiet footsteps, the weight on the bed, and then the purr would start up before we could even see him. He’d spend all night with us most nights, laying on Ash’s chest in the classic Sphinx pose or curled up behind their knees under the blanket.

I loved how frequently he showed up already purring, apparently anticipating how good of a time he was about to have. It came across as this comical overconfidence, like he took for granted that of course he would be involved in whatever Ash was doing. But his purr, as common and subdued as it was, was such a deep and full and genuine rumble. He made me feel like I’d earned it, like I must’ve done something truly admirable to earn this level of praise. I always called it regal. The purr of a king.

In the early morning hours of October 13, early enough that it was still the previous night, Twigs came downstairs and yelled. That wasn’t unusual; he’d yell for Ash’s attention all the time. But then he lay on his stomach, angled straight up like the actual Sphinx, a pose he exclusively reserved for comfy places like laps and cat beds.

Ash and I went over to check him out, but we couldn’t find any tender spots, injuries, or other obvious problems. My best guess was a stomachache, which wasn’t unheard of for Twigs; perhaps laying on his stomach helped settle it? The room was a little chilly and he wasn’t wearing a sweater, so Ash wrapped him in a blanket and set him on the beanbag he liked, in the path of a heat lamp.

We went to bed only an hour or so later, and Ash carried Twigs with them. Without the heat lamp on him, he was noticeably cold to the touch now, and starting to stumble. I didn’t think of it until later, but as cold as he was, he never shivered once.

We rushed him to a 24/7 emergency vet.

His temperature was 92 when we arrived. Normal body temperature for cats is around 100.

They set about warming him up, rushed through some authorizations, drew some blood, told us results would come in about thirty minutes.

Twigs didn’t make it that long. At 4:26 in the morning, cold and confused, somewhere in a sterile room apart from everyone he’d ever known and loved, his heart stopped.

Only three or four hours had passed since he first showed any signs of distress whatsoever, and Twigs was gone.

Twigs was so expressive! He had so much personality, and he showed all of it. Sphynxes seem a little easier to read than furred cats, but… well, Pearl is a little reserved, and Anise is downright incomprehensible. Twigs was an open book.

Photos don’t quite do him justice, since cats are easiest to photograph when they’re relaxing. All of his body language and facial expressions felt really crisp and distinct, like he wanted you to know what he was thinking, but didn’t want to ham it up. How do I even explain this? How would I explain the faces a human makes, even?

His “I love sitting on you” face, his “I want to eat that” face, his “this is a bit annoying but I’ll put up with it” face… they were all so clear and distinct, moreso than any of our other cats, moreso than any cat I’ve met. He’d even turn up the corners of his mouth when he was really happy, making a little cat smile.

His eyes were huge and beautiful, and we got to see them a lot while he played sentinel, perched somewhere with a good field of view. They were different colors, too! Only slightly, but in the right light, one was distinctly greener and the other distinctly bluer. It was obvious from a glance at his eyes whether he was staring into space, watching you, wanting something from you, or wanting to come over to you.

He was always, always delighted when someone would pet him. I don’t think Twigs ever acted solitary; he stands out as the most readily and consistently affectionate cat we’ve had. He even had a specific expression for when he was in a good mood and wanted someone to pet him, which I called “bedroom eyes” — both because he lidded his eyes a bit, and because he mostly did it when laying in bed with us. If he was especially happy, he’d come lie on your chest, scoot forwards as far as he possibly could, and give you super nuzzles all over your chin.

Twigs had a very pettable head, too. Broad, with his ears more to the sides. I always said he had a cheese head, because it reminded me of a cheese wedge? For some reason? He had a good cheese head, perfect for kissing (“kitten kisses”), which he seemed to understand was a sign of affection. He loved having his head pet so much that he’d keep tilting his head further and further back, ostensibly to press harder against your hand — but if he was perched on the top level of a cat tree, that made it harder to reach the top of his head, so you’d have to do this silly little negotiation with him. It made his smile all the easier to see, though.

He had some other quirky little “tells” that seemed subtle, but that gave away what he’d almost certainly do next: hesitating in a particular way before inexplicably dashing away, or looking up and around at the ceiling before doing a big meow.

His meows! Twigs had a huge vocabulary, and so much of it was for asking politely for things. His “yell” for when he wanted Ash was big and boisterous, with a little characteristic warble to it, and he opened his mouth comically wide when he did it. If he wanted Ash’s steak scraps (which he loved), he had a very reserved meow for asking for them. If he couldn’t get under a blanket, he had a different reserved meow for asking for help. He was the only cat who regularly did that funny chirpy meow at bugs on the wall, though we hadn’t heard that one since we left the Seattle area — Vegas didn’t have nearly as many bugs.

When Anise would roughhouse a bit too hard, Twigs had a distinct pained meow for “this is too much” that would bring one of us running. I didn’t hear it much after we got Cheeseball, who acts as a more eager sparring partner for Anise, until one day I heard a distorted version of it — and I found Twigs and Cheeseball happily wrestling! Twigs came up with a new meow, ending on a happy note rather than a painful one, just for when he was playing with this new giant kitten friend.

One of the most frustrating parts of this is that it’s so hard to capture a cat’s meows, or a lot of other subtleties. As vocal as Twigs was, he still only spoke when he had something to say, and that was rarely when he was in front of a camera. I remember them so clearly now, but how can I convey them in text? Myehhh doesn’t really cut it. (I’ve been sorting through old cat videos, but it’s slow going; I’ll throw some of them up somewhere in the near future.)

I don’t understand what happened.

The test results only showed that he was severely anemic — he had far too few red blood cells, so he couldn’t warm himself or get enough oxygen. They didn’t explain how he’d reached that point in a matter of hours without showing milder symptoms first.

The day had been entirely normal. Twigs had been happy and active earlier in the afternoon. He wasn’t in the habit of chewing or eating strange things. We keep all our cats indoors, and the others are still fine, so he couldn’t have picked up a communicable illness. If he’d ever shown any sign that anything was wrong, I know with absolute certainty that Ash would’ve noticed, just as I immediately noticed when my cat Styx had lost weight. But there was nothing.

What, then, actually happened to him? I don’t know. I’ll never know. I briefly thought to ask for an autopsy, but at the time, I couldn’t bear the thought of what that would… mean.

No explanation, no reason, nothing to blame. Twigs was his healthy happy self all day, all week, all month, all year. Right up until he wasn’t. And then he died.

Twigs was so friendly. Kind, even. He never hurt anyone; he rarely did anything unexpected or rambunctious. He rarely even messed with things he shouldn’t, in sharp contrast to Anise, who tries to push my phone off my desk anytime he wants my attention; the most Twigs would do was gingerly tap something with a paw to see if it would react, then move on.

(Well, with one exception. If he found an unguarded glass of water, but the water level was too low for him to reach it, he was smart enough to tip the whole glass over and douse everything on your desk. We switched to reusable water bottles years ago.)

I can’t think of a single time Twigs was mean or angry or even wanted to be alone. All the cats have times they’re comfortable and don’t want to be disturbed, or just aren’t in the mood, or whatever — except Twigs.

If Ash scolded him (“Twigs!”), he’d dash off to a cat tree and scrabble at it briefly, taking his frustrations out with a few quick scratches and this funny little shimmy of his hips, then forget all about it. In extreme cases, he might run upstairs to our empty bedroom, yell once or twice, then come back down. Or in milder cases, when he couldn’t get something he wanted, he’d snort audibly and that was that. It was so, so charming — if he was upset, all he needed to do was go somewhere to yell about it for a moment, and then he was fine.

He was so patient, too. Ash put little costumes on him a few times, which he took in stride — well, for a cat, at least. He was always happy to be picked up, wrapped in clothing or a blanket, and/or held in all manner of silly positions. You could check his teeth and he’d hardly mind at all. Play with his ears, shake his paw, squish his lip, whatever; he was content just to be interacted with. (I suspect there was some mutual reinforcement between Ash doing goofy things to Twigs, and Twigs laying in increasingly obnoxious ways on Ash.)

He didn’t much like having his claws trimmed, and when Ash would do it, he used to bite the squishy part of their thumb — but not bite down, only put his teeth around their hand. Enough to communicate “I don’t like this” without trying to hurt them. Ash eventually started bribing him with cat treats every few claws, and then he disliked the process a bit less.

His good nature extended to the other cats, as well. He befriended every cat we’ve ever had! I didn’t really think about it until after he died, but if I ever saw two or more cats hanging out together, Twigs was almost guaranteed to be one of them. He was the binding force of our little cat sitcom.

There was one brief exception, when Ash first adopted Pearl — the first new cat since Twigs that was 100% Ash’s. They kept Pearl with them all the time at first, and Twigs got so jealous. Very early on he made his feelings very clear: he stood on the other side of the room, stared right at Ash (and Pearl), and made a huge meow at them. Then after like three days he found out that he and Pearl could both fit in Ash’s lap and everything was fine.

He’d cozy up with Anise or Pearl for warmth, and we’d often see all three of them nestled together, as though Twigs’s soothing presence deterred Anise and Pearl from their usual squabbling. He had an awkward but friendly relationship with Napoleon, the most aloof of the cats by far, who doesn’t show much affection towards any of the others except Pearl. I remember Napoleon used to refuse to groom Twigs anywhere but on the backs of his ears (the only place he had fur!), but after some years together, we started to see Napoleon grooming Twigs’s face and neck as well. For Napoleon, that was a really close friendship.

Twigs was even friends with Apollo, the German shepherd we used to have, who was much bigger than this tiny bald cat. I have a video of Twigs and Apollo playing, where Apollo is gently nudging Twigs around with his nose and Twigs alternates between nuzzling and lightly smacking Apollo. What a sweetheart. I don’t think any other cat interacted with Apollo quite like that.

He had a somewhat more complicated dynamic with Anise, who’s a good bit rowdier and more… destructive. Anise liked to start little brawls a lot, which wasn’t quite Twigs’s usual style, but he’d play along until Anise got too rough. (It probably didn’t help that Twigs would often respond by grabbing Anise by the sweater, which allowed Anise to wriggle backwards out of it and unleash his full powers.)

It’s been funny looking at older photos; when we first got Anise, Twigs was pristine, with maybe a scar or two on his haunch somewhere. (And all down the top of his tail, which he liked to nibble with some intensity.) At the end of his life, Twigs was riddled with little round scars from where Anise had bitten his back, and even a conspicuous dark spot right on top of his head. Who bites someone’s head?

I don’t remember his relationship with Styx as clearly, but I have enough photo evidence of it. The two of them were very close and spent a lot of time snuggled together, whether sleeping or just hanging out. We even got them matching pink sweaters! I’d forgotten that was deliberate. They played together, too, though much less seriously than Anise and on more “even” terms.

Six and a half years ago, my own cat Styx died. He’d been my cat, the way Twigs was Ash’s cat, sticking to me like glue the whole time I had him. But then Styx contracted a cruel and incurable illness, one that can strike even indoor cats and prefers to take the young. He wasted away over the course of a month.

I’d like to think that, whatever it was that took Twigs from us, maybe this swift departure saved him from the kind of long and excruciating ordeal that Styx went through.

I wrote his eulogy the day after he died. I avoided looking at it for years, but finally went back and read it a few days ago. It seemed so short! Was that really all I had to say about him? I knew him for over a year, yet I feel like I barely got to know him — I think Cheeseball is already older than Styx was when he died, and Cheeseball’s personality is still rapidly developing.

I was more shocked to find my own tweets from soon after Styx’s death, saying I couldn’t even look at photos of him. How long did that last? I don’t remember.

It hurt too much, so I avoided his memory, and now so much of it is a fragmented blur. Watching him deterioriate was gut-wrenching, and the worst part of his life — but it’s what I spilled the most ink on, and the part I need the least help remembering. Why did I write so much about that month? None of it was important in the end, yet I liveblogged every gratuitous medical detail. I guess I didn’t know what else to do, watching Styx wither away in my arms, while I couldn’t do anything about it.

I still cry for him, sometimes. I get a little sad over something else, and I remember Styx, and I cry. No matter how many of the details fade, I know I had a little cat named Styx who I loved dearly, and he loved me back.

This feels like a second chance, though. I won’t make the same mistake again.

It was hard to grieve with Ash all those years ago, back when things were so awkward. Now we can mourn together, and thinking about Twigs doesn’t sting the way thinking about Styx used to. It finally feels okay to remember Styx, too, and I’ve been rediscovering some old moments as I’ve sorted through photos in search of Twigs.

We’ve been celebrating and filling our space with both of them — we printed out physical copies of our favorite photo of each and put them in little thematic frames. Their pawprint casts are together on a shelf behind Ash’s desk. Nearby is Twigs’s urn, and I’d like to put Styx’s humble grave marker next to it, once I figure out where I packed it. Ash is painting portraits of them.

At my suggestion, we threw Twigs a little goodbye party — I baked a pumpkin cake (in honor of his homemade pumpkin cat food and the one fall he loved a tiny pumpkin), Ash decorated it, and we talked about Twigs and all the things about him that we miss. I insisted we wear party hats.

I’ve been taking notes on his life ever since he died, all so I could write this eulogy for him. It’s intimidating and even more difficult than I expected, trying to capture a life that meant so much to us in only a few thousand words. I hope I’m doing him justice. I want everyone to know how good Twigs was, and how much we’ve lost.

Twigs had his sassy side, but it was always sweet and harmless. Less like typical cat aloofness, more like that charming confidence of showing up to cuddle with his purr already in full swing, completely taking for granted that he was welcome and was about to enjoy himself. Or the similar energy he put on display when you were on a couch and he wanted to sit on you: he’d identify the most Twigs-shaped nook on your body and wedge his butt backwards into it, sometimes even hoisting himself with his front legs a bit, like a human settling into a recliner.

For example: if Twigs tried to approach Ash but Ash pushed him away — e.g., because they were eating or painting or their lap was occupied — then Twigs would often do a complete circle around the table or part of the room, only to approach Ash again from the other direction. It was so comical! So gentle and friendly, but cheerfully defiant about being near Ash. As if he couldn’t even imagine that he was disallowed for the moment. The problem must have been with his approach. There’s just no other rational explanation.

Since living in Colorado, we’ve occasionally come home and opened the front door only for Twigs to immediately dart outside… just so he could cross the front porch, stop at the nearest blade of grass, and bite it. None of the other cats have ever shown any interest in grass, but every once in a great while, Twigs would just get a hankering, and it’s the only reason he’s ever so much as attempted to leave the house. (Thank goodness.)

The thing that hit hardest right after he died was the feeding routine. Several of the cats eat storebought food, kept out of reach in a big dog cage we bought for this purpose, while Pearl and Twigs share homemade food. For the last couple months, whenever I went to go open the cage to let the other cats in, Twigs would trot along with them! He wouldn’t actually go in the cage, and he’d even slow down before getting to it (so the others would get ahead and it’d be easy to keep him out), but he acted like he belonged inside. It was such a perfect reflection of his personality: he went after something he wanted, yet he stopped short of breaking the rules.

Twigs knew how to have a good time, too. He loved rollin’ around on carpet. He’d wriggle on his back, grab the carpet with his claws and pull himself along it, and clearly be having the time of his life. Our Vegas home didn’t have any carpeted floors, but we added a little carpeted platform to the stairs (so the cats wouldn’t fall off!) and he had just as good a time on that. Later we got some small cat trees with singular round platforms, and those had a carpet texture he loved as well.

Rollin’ around would put Twigs in a feisty mood, and he’d reach out to smack anyone — cat, dog, or human — who came nearby. Ash would make a game out of this: they’d tap the floor nearby or the edge of the platform, then try to pull their hand away before Twigs “got” them. Sometimes Twigs would make a very riled-up face but not try to get you, and you could wind him up a little more by performing the “cat pat” — lightly and repeatedly tapping his haunch with your fingertips. You could watch him get more rowdy in real time, and then the game was to stop before he suddenly rolled over and tried to grab your hand.

Our home near Seattle was just up the street from a big park, and on a couple occasions, Ash took Twigs out for a walk on a little leash. On one such walk, while I was holding Twigs’s leash, he suddenly darted straight away from me and towards some underbrush! The leash caught him, of course, but he was running so fast that it actually yanked him right off the ground and flipped him over. (He was fine, albeit just as surprised as we were!)

(On another walk, Twigs stood right in front of Ash and made a huge myeehhhh up at them, clearly indicating that he was Done With Outside For Now. Poor baby. Ash picked him up, wrapped him in their sweatshirt, and held him until we got home. He really knew how to say exactly what he was thinking.)

Twigs played the typical cat games as well, when he felt like it — he might join in when we were playing string with Pearl, or teleport into the room when the laser pointer came out. One of the last things Twigs played with was a tiny mouse toy, ripped and with its stuffing pouring out. He sometimes liked to carry them around, roll around on the floor fighting them, then carry them somewhere else and do it again. He had a surprising ferocity with toys at times: wild eyes and incredibly quick pounces! It made me appreciate all the more how gentle he was with cats and people.

Once in a great while he’d play fetch, repeatedly bringing the same toy (or twist-tie or something) back to Ash’s feet so they could toss it and he could pounce it again. I even have an old video of Twigs playing chase: Ash would dash down the hallway, Twigs would dart after them with an intensely serious expression, Ash would yelp that Twigs “caught” them, and then they’d run down the hallway the other way. I don’t think any other cat we’ve had has really done that! They’ll run away from us, but not try to chase us around.

(Ash put fantasy “Luneko” versions of all our cats in NEON PHASE, a little game we made a few years ago, and I was struck by how Branch Commander Twig’s personality was so serious, when Twigs struck me as mostly lighthearted and friendly. But then, I suppose Twigs was very serious — about being lighthearted and friendly.)

I can’t tell what effect this has had on the other cats. They were all friendly with Twigs. Do they wonder where he is? Do they, too, assume he’s out of sight somewhere? Are they grieving? Will they grieve later?

The other cats got to saw Styx’s body, but Twigs died elsewhere. We have no way to tell them what happened to him. They just have to… guess? After living their whole lives with him? That sucks.

I think they’ve been more affectionate over the past week or so. Or they might be cuddling more because it’s getting colder. Or I might be paying more attention to them. Hard to say.

They do seem to be expanding their roles to fill Twigs’s niche. Napoleon, best known for spending almost all his time alone, has come and hung out on the couch — virtually unheard of. Anise and Cheeseball are, well, fighting each other instead of both fighting Twigs — but they’re starting fewer fights with Pearl. Pearl, who has had absolutely no tolerance for Anise since we left Vegas, has spent whole nights asleep next to him without making a fuss.

I guess they learned a lot from him.

Twigs was also fiercely loyal, but thankfully only had to show it a couple times.

We spent last summer in Marl’s parents’ unused (finished) basement, where they kept four cats of their own. (For a total of nine. We had quite a time.) One of them, Seamus, kept antagonizing our only furry cat, Napoleon.

We aren’t really sure how or why this started, but every so often, Seamus would start chasing Napoleon around, and Napoleon would scream. I don’t know why Napoleon was so scared of him, or what Seamus thought he was doing, or why he couldn’t understand that Napoleon didn’t like it. It was a constant source of stress for everyone; Seamus did it infrequently but seemingly on a whim, and we didn’t have many options for segregating the cats outright.

The incredible thing was, every time Seamus would start chasing Napoleon… Twigs would start chasing Seamus. And then Pearl would chase along with Twigs. And this would often end with Twigs and Pearl facing Seamus down, with Twigs saying some very nasty things that I will not repeat here.

(Anise would often show up and also run around, but he didn’t seem to understand why everyone was making such a fuss. While Twigs and Pearl were cornering Seamus, Anise would be standing next to them while mostly looking confused. Hey guys I see we’re playing chase!! I love chase too!! Oh why’d we all stop?)

I wouldn’t say it helped matters much, but it was strangely heartwarming. Twigs considered Napoleon his friend and had no problem telling this strange bully cat, a Maine Coon twice his size, to fuck right off.

Oh, but that’s nothing.

Apollo, that German shepherd we used to have, once somehow managed to knock down a whole set of shelves in Ash’s room. Ash, of course, yelled his name in response. They must’ve sounded really mad, because Twigs appeared instantly. He stood right in front of Apollo (separating him from Ash), in a very aggressive stance, making some very threatening growls and meows.

And he chased Apollo out of the room and right down the hallway.

All Twigs knew was that Apollo had seriously upset Ash, and that was that. No questions asked. This tiny little cat stood up to a giant wolf, because he thought Ash needed defending. Twigs was never aggressive or mean towards Apollo any other time, before or since. This only happened once, once ever, when Twigs thought Ash was in danger.

What a brave cat! If Apollo had wished Ash (or Twigs) harm, well, I don’t like those odds. But Twigs didn’t even think twice. We’ve never stopped marvelling over it.

I say “brave” very deliberately, because Twigs while was not fearless, he stood up to his fears. The only one we really saw was a fear of, ah, foam strips. See, we used to have a tiny “gym” in the corner of the kitchen, and the equipment sat on a foam mat made out of tiles with jigsaw edges that could fit together. To give the assembled mat a smooth perimeter, the tiles also came with thin edge pieces.

Foot traffic (or cats) could knock one of the edge pieces loose, leaving a strip of black foam alone on the floor. Twigs found this highly alarming. He would crouch down and eye it very suspiciously, creep up to give it a light smack and then back off, and generally treat it like a live wire. We assume it looked like a snake to him, though no other cat took interest in the edge pieces except to play with them, and Twigs never reacted the same way to anything else snake-shaped.

But he didn’t run away. He investigated, to see if it was dangerous, see if there was a predator in his home. Even after we’d find him doing this and put the foam piece back, Twigs would creep around for a while, looking for possible snakes until he was convinced it was gone. He was clearly very wary, yet he never ran, never hid.

The only other times I recall seeing Twigs anything close to scared were when he encountered a couple of accessories that resembled large animals: a Lucario hat Ash bought many years ago, and one of those goofy horse masks. I’m not even sure if “scared” is even the right word; he looked more annoyed? He neither backed down nor tried to attack them. I only remember him standing his ground and hissing, warning them to leave him alone.

I never heard him hiss any other time.

(Ash did, though. Once as a tiny kitten, our late cat Granite sat on him. A big furry cat just sat his ass right down on this little kitten. Kitten Twigs hissed about this, but kittens aren’t very ferocious hissers, so it came out khh! khh!, which Granite ignored.Funnily enough, once Twigs grew up, he developed his own habit of sitting on furred cats!)

We haven’t had a death since Styx. Twigs’s best friend! I never once expected Twigs would be the next to go. Now Napoleon is the only one left of the original crew.

Ash moved in with me not long after adopting Twigs. I don’t think he was even a year old. I knew him his entire adult life! I lived with him longer than I’ve lived with anyone, save my parents as a kid.

For so many years, it’s been Ash and Twigs. The inseparable duo, joined at the hip. I knew it would end someday, but I was so sure that day was much further off. I thought he’d be around for another five years at least, and secretly hoped he’d make it another ten. But we only got half of that. He loved twice as hard, and his heart burned out far too early.

He had so much life left in him. He played, he ran around, he wrestled (or, at least, was wrestled upon). He was still growing, inventing new antics and new ways to interact with us.

It’s been a strange experience. I couldn’t even absorb the factual knowledge of his death at first, even as I spent much of the first few days crying. How could Twigs die? That doesn’t make any sense; I haven’t seen him yet today, but he’ll show up soon. But I feel really sad. Oh, right, that’s because Twigs died. Rinse, repeat, over and over.

We picked his ashes a few days later. It’s been nice to have him home again, and it helps to have something physical to look at, rather than just the lack of his presence. Ash intends to paint his urn.

It got easier much more quickly than I expected, and that’s been weird as well. I wanted to hold onto his memory and be happy for the time I got to spend with him, and then that actually happened. I think about him a lot (especially over the multiple days it’s taken to write this), and a lot of little things remind me of him, but they don’t make me break down in tears. Usually.

That feels a little bad. But I know that hurting less doesn’t mean I loved him any less. And I know the last thing Twigs would want is for us to be sad.

Twigs was the best. I miss so much about him. I miss the way his whole nose scrunched up when he did a big meow. I miss his distinct little trot as he came down the hallway to see you. I miss watching him do eager little circles on the floor as I got the food out. I miss how he’d smack his lips as he showed up, a distinct and inexplicable quirk I’ve never seen in any other cat, a good compliment to how long he’d spend licking his chops after eating. I miss his huge ears! I miss “savannah cat” — when he’d hook his paws over the edge of something he was lying on, like an arm or the edge of a cat bed or the corner of my computer tower. I miss what a serene and calming presence he was.

It’s funny how some of the most memorable moments are things he only did one time. He joined Ash in the bathtub once — they were reading a book and Twigs came in, hopped in the bath, and sat in water up to his neck, just to be with them. He often announced his presence with a questioning meow when coming into Ash’s Vegas room at night, and once he did this really funny “meow-ow!” kind of double meow, and we’ve repeated it to each other as a nod to Twigs ever since, even though he never did it again.

One fall, we got a tiny pumpkin — the size of a slightly disappointing donut — and Twigs was enamored with it. We’d roll it along its edge and he’d chase after it and keep biting it, and it was so cute. Another fall, we bought another one, and Twigs wasn’t interested in it at all. Very cutting-edge of him. Tiny pumpkin is so last year.

He used to be really interested in eggs, too. For a while, we couldn’t turn our backs on an egg on the counter, because Twigs would materialize and start gently batting it around. Then he lost interest.

I miss how he slept with me. He’d always slept either behind my knees or on top of the covers, but right towards the end of his life, he invented a new trick, just for me. I sleep on my side, so he couldn’t lay on my chest; instead, he went under the covers, poked his head out, and lay against my chest with his head on my pillow. Like a little person! It was so sweet. He’d then keep nuzzling my face with his cold wet nose, which was kind of annoying. I miss that, too.

Even the annoying things are conspicuously absent. He frequently stepped on my hair while I was in bed, trying to get around me to get to Ash, and wow that is painful. Twigs groomed his cat sweaters more intensely than any other cat, biting the fabric and pulling so hard that it stretched and made this horrible high-pitched squeak, like nails on a chalkboard. He loved to groom people, too — usually on the chin or upper chest, since that’s what was accessible when he lay on you. Somehow Ash got used to it (and learned to redirect him to their palm, which he’d lick for ages), but I could never bear more than a few seconds of his cheese grater tongue.

What a good cat.

I felt like I’d been waiting for this all year. I don’t want to go much into it, but death has felt like a looming spectre almost since we moved in. The pointlessness of doing things, the feeling that I’m just passing time waiting to die, the occasional intrusive thought about a tragic accident befalling one of us or one of the cats. Never Twigs, though.

Last year was harder on me than I thought. I fired on all cylinders, trying to get Ash back on their feet, and once that happened… I deflated and never quite recovered. I lost a lot of my drive, my spark, my voice. I got frustrated with difficult work much more easily. I stopped writing. I stopped interacting. I stopped trying.

I didn’t even realize. Even as I felt increasingly distant and detached from the universe, I still thought I’d been pretty normal all year with only a few rough patches. It’s been hard to compare the past to the present, separated as they are by a strange and tumultuous six months that changed almost everything. Then Ash commented that I’d seemed kind of down all year. What a jolt that was, and only a few days before Twigs died.

Twigs’s death feels like a kick in the ass. I’ve felt a lot of despair over the past year, but all of it has been tied to anxieties and what-ifs — imaginary things. But this is sad, which is very different. This carries a pain for something tangible, something real, something important, something I want to hold onto. How can any of my little fantasy fears matter, when the loss of a cat outweighs all of them combined?

I don’t want to waste any more time. I want to reflect what I admired about Twigs: kind, patient, confident, and loving. I want to make this mean something.

Twigs had a good life. He spent it around people and cats he loved dearly, and who loved him right back. He had friends when he was lonely and blankets when he was chilly.

Oh, did he ever love blankets. Sphynxes are naked and tend to seek out warmth, of course, but of the four we’ve had, Twigs was by far the one who treated heat sources like a passion rather than mere physical comfort. His ability to identify the most snuggly spot to back his ass into was nothing short of superfeline. Sometimes he’d toast himself so well that he turned a little pink! And he used to do this incredible display of cat paws, with all four paws, accompanied by the occasional meow — but only on a specific blanket that we’ve long since lost.

He was also the one who tolerated cat sweaters the best (despite inflicting the most destruction on them). Anise’s powers of antagonism are greatly reduced in a sweater, and he will run away if he sees you approaching him with one; Pearl still does a funny awkward walk with her back half lower to the ground, even after wearing them through half a dozen winters. But Twigs in a sweater just acted like Twigs.

And what a well-travelled cat! He lived in four states and drove through half a dozen others. That’s more of the world than a decent number of humans see. He got to meet and snuggle with all kinds of other cats, and even some sort of giant wolf-cat who tried to herd him occasionally. He got to see the great outdoors, then decided he didn’t like it and returned to the great indoors.

Twigs did spend a couple of his later years afflicted with “pillow paw” — his pawpads swelled up one day, for seemingly no reason. Our vet couldn’t find an underlying cause, and meanwhile it was uncomfortable for him to land on his feet from a height. Poor guy. I’m eternally grateful to the vet we found last summer, who finally solved the mystery and cured him. He got to spend his final year active and unhindered again.

Ash spent much of our last couple Vegas years secluded in their office, too, so Twigs didn’t get as much face time as I’m sure he would’ve liked. But in our new place, both of our desks are out in the open and right next to each other, so Twigs could see them whenever he wanted. Sometimes he lay on a cat bed on my desk watching them, or strolled back and forth between us both, purring up a storm.

It’s been a bit of a rollercoaster for all of us, but I think the last year was the best year of his life.

I miss Twigs, but I smile when I think about him. He made us so happy while he was here.

Twigs came into Ash’s life while they were somewhat adrift — no clear goals, no home of their own, resigned to an unhappy marriage. He stuck with them for nine whole years, unwavering in his affection. He followed them down into the darkness, down where they couldn’t feel love from anyone — anyone except Twigs.

Now Ash has work and a community they love. We have a home together, and it finally feels like one. And by sheer coincidence, Ash’s divorce was finalized mere days after Twigs died. His entire life was contained within that marriage, from birth to death.

(Oh, we’re married now. Hurrah.)

Ash adopted Twigs almost on a whim, and he left us just as abruptly. As though he’d only shown up in the first place to help Ash when they needed it, and with Marl finally out of our lives, his work here was done.

The last thing Twigs did, the night that he died, was tell us he loved us. Ash put him under the blanket to try warming him up, and at first he was by our feet… but then he crawled up to slump against me, similar to how he did when I was alone in bed, and then he climbed on Ash’s chest and lay on them for a moment. Right at the end, as cold and confused as he must’ve felt, all he wanted was to be with Ash, to be with both of us.

I don’t know where Twigs is, now. He might be nowhere. But the universe has consistently proven itself to be more baffling and beautiful than I expect, so I’ll hold out hope that he’s somewhere — somewhere he can once again see Styx, his (other) best friend in the whole wide world. Somewhere that we can see them both again, one day.

Goodbye, Twigs! We’ll always love you, and we’ll always miss you.

Thank you, so much, for everything.

A colorful and abstract painting of Twigs

Weekly roundup: Foglights

Post Syndicated from Eevee original https://eev.ee/dev/2019/09/22/weekly-roundup-foglights/

Hello!! This is just the year of endless interruptions. I switched medication and I’m functional, but I think I have withdrawal from going off the old stuff, so I’ve been a little spacey for about a week. Hoping it passes soon! Also some other distractions happened. But in the meantime I’ve been drawing a lot.

  • art: I’ve been joining Ash’s commission streams for the past week or so and mostly doodling porn, but after doing that for a while, I decided I should try coloring stuff again, so now I’m doing that also. Definitely need the practice, but really enjoying seeing myself produce more finished work again. I guess I could go put some of that work in the canonical place, too.

  • blog: I did a whole bunch of work on a blog post which is going to be preposterously long, but hey, what a way to come back. Hoping to finish it by the end of the month, if I can get my brain working again.

  • alice: Still writing for this…

Weekly roundup: All that glistens

Post Syndicated from Eevee original https://eev.ee/dev/2019/09/12/weekly-roundup-all-that-glistens/

  • fox flux: I’ve been kind of taking a break from physics, but I did have some ideas about how extrinsic velocity could work, got them working for a conveyor belt, and then extended the same concept to a rough rework of pushing. It works surprisingly well given how little time I spent on it, so that’s very promising.

  • gleam: I put together the first production VN with it, and although I had to cheat and hand-edit a bit, GLEAM grew a bunch of useful stubs of features along the way! It’s getting there. Also I discovered a fascinating edge case in Firefox when you have 800 images visible but all but one of them have zero opacity.

Old CSS, new CSS

Post Syndicated from Eevee original https://eev.ee/blog/2019/09/07/old-css-new-css/

I first got into web design/development in the late 90s, and only as I type this sentence do I realize how long ago that was.

And boy, it was horrendous. I mean, being able to make stuff and put it online where other people could see it was pretty slick, but we did not have very much to work with.

I’ve been taking for granted that most folks doing web stuff still remember those days, or at least the decade that followed, but I think that assumption might be a wee bit out of date. A little while ago I encountered a tweet incredulous at the lack of border-radius back in the day. I still remember waiting with bated breath for it to be unprefixed!

But then, I suspect I also know a number of folks who only tried web design in the old days, and assume nothing about it has changed since.

I’m here to tell all of you to get off my lawn. Here’s a history of CSS and web design, as I remember it.


The very early days

In the beginning, there was no CSS. This was very bad.

My favorite artifact of this era is the book I learned HTML from, HTML: The Definitive Guide, published in several editions in the mid to late 90s. The book was indeed about HTML, with no mention of CSS at all. I don’t have it any more and can’t readily find screenshots online, but here’s a page from HTML & XHTML: The Definitive Guide, which seems to be a revision (I’ll get to XHTML later) with much the same style. Here, then, is the cutting-edge web design advice of 199X:

Screenshot of a plain website in IE, with plain black text on a white background with a simple image

Clearly delineate headers and footers with horizontal rules.

No, that’s not a border-top. That’s an <hr>. The page title is almost certainly centered with, well, <center>.

The page uses the default text color, background, and font. Partly because this is a guidebook introducing concepts one at a time; partly because the book was printed in black and white; and partly, I’m sure, because it reflected the reality that coloring anything was a huge pain in the ass.

Let’s say you wanted all your <h1>s to be red, across your entire site. You had to do this:

1
<H1><FONT COLOR=red>...</FONT></H1>

every single goddamn time. Hope you never decide to switch to blue!

Oh, and everyone wrote HTML tags in all caps. I don’t remember why we all thought that was a good idea. Maybe this was before syntax highlighting in text editors was very common (read: I was 12 and using Notepad), and uppercase tags were easier to distinguish from body text.

Keeping your site consistent was thus something of a nightmare. One solution was to simply not style anything, which a lot of folks did. This was nice, in some ways, since browsers let you change those defaults, so you could read the web how you wanted.

A clever alternate solution, which I remember showing up in a lot of Geocities sites, was to simply give every page a completely different visual style. Fuck it, right? Just do whatever you want on each new page.

That trend was quite possibly the height of web design.

Damn, I miss those days. There were no big walled gardens, no Twitter or Facebook. If you had anything to say to anyone, you had to put together your own website. It was amazing. No one knew what they were doing; I’d wager that the vast majority of web designers at the time were clueless hobbyist tweens (like me) all copying from other clueless hobbyist tweens. Half the web was fan portals about Animorphs, with inexplicable splash pages warning you that their site worked best if you had a 640×480 screen. (Anyone else should, I don’t know, get a new monitor?) Everyone who was cool and in the know used Internet Explorer 3, the most advanced browser, but some losers still used Netscape Navigator so you had to put a “Best in IE” animated GIF on your splash page too.

This was also the era of “web-safe colors” — a palette of 216 colors, where every channel was one of 00, 33, 66, 99, cc, or ff — which existed because some people still had 256-color monitors! The things we take for granted now, like 24-bit color.

In fact, a lot of stuff we take for granted now was still a strange and untamed problem space. You want to have the same navigation on every page on your website? Okay, no problem: copy/paste it onto each page. When you update it, be sure to update every page — but most likely you’ll forget some, and your whole site will become an archaeological dig into itself, with strata of increasingly bitrotted pages.

Much easier was to use frames, meaning the browser window is split into a grid and a different page loads in each section… but then people would get confused if they landed on an individual page without the frames, as was common when coming from a search engine like AltaVista. (I can’t believe I’m explaining frames, but no one has used them since like 2001. You know iframes? The “i” is for inline, to distinguish them from regular frames, which take up the entire viewport.)

PHP wasn’t even called that yet, and nobody had heard of it. This weird “Perl” and “CGI” thing was really strange and hard to understand, and it didn’t work on your own computer, and the errors were hard to find and diagnose, and anyway Geocities didn’t support it. If you were really lucky and smart, your web host used Apache, and you could use its “server side include” syntax to do something like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<BODY>
    <TABLE WIDTH=100% BORDER=0 CELLSPACING=8 CELLPADDING=0>
        <TR>
            <TD COLSPAN=2>
                <!--#include virtual="/header.html" --> 
            </TD>
        </TR>
        <TR>
            <TD WIDTH=20%>
                <!--#include virtual="/navigation.html" --> 
            </TD>
            <TD>
                (actual page content goes here)
            </TD>
        </TR>
    </TABLE>
</BODY>

Mwah. Beautiful. Apache would see the special comments, paste in the contents of the referenced files, and you’re off to the races. The downside was that when you wanted to work on your site, all the navigation was missing, because you were doing it on your regular computer without Apache, and your web browser thought those were just regular HTML comments. It was impossible to install Apache, of course, because you had a computer, not a server.

Sadly, that’s all gone now — paved over by homogenous timelines where anything that wasn’t made this week is old news and long forgotten. The web was supposed to make information eternal, but instead, so much of it became ephemeral. I miss when virtually everyone I knew had their own website. Having a Twitter and an Instagram as your entire online presence is a poor substitute.

So let’s look at the Space Jam website.

Case study: Space Jam

Space Jam, if you’re not aware, is the greatest movie of all time and focuses on Bugs Bunny’s brief basketball career, playing alongside a live action Michael Jordan. It was followed by a series of very successful and high-praised RPG spinoffs, which tell the story of the fallout of the Space Jam and are extremely canon.

And we are truly blessed, for 23 years after it came out, its website is STILL UP. We can explore the pinnacle of 1996 web design, right here, right now.

First, notice that every page of this site is a static page. Not only that, but it’s a static page ending in .htm rather than .html, because people on Windows versions before 95 were still beholden to 8.3 filenames. Not really sure why that mattered in a URL, but there you go.

The CSS for the splash page looks like this:

1
<body bgcolor="#000000" background="img/bg_stars.gif" text="#ff0000" link="#ff4c4c" vlink="#ff4c4c" alink="#ff4c4c">

Haha, just kidding! There’s no CSS at all. I see a single line in the page source, but I’m pretty sure that was added much later to style some policy links.

Next, notice the extremely precise positioning of these navigation links. This feat was accomplished the same way everyone did everything in 1996: with tables.

In fact, tables have one advantage over CSS for layout: you can ctrl-click to select a table cell and drag around to select all of them, which shows you how the cells are arranged and is kind of like a super retro layout debugger. This was great because the first meaningful web debug tool, Firebug, wasn’t released until 2006 — a whole ten years later!

Screenshot of the Space Jam website with the navigation table's cells selected, showing how the layout works

The markup for this table is overflowing with inexplicable blank lines, but with those removed, it looks like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<table width=500 border=0>
<TR>
<TD colspan=5 align=right valign=top>
</td></tr>
<tr>
<td colspan=2 align=right valign=middle>
<br>
<br>
<br>
<a href="cmp/pressbox/pressboxframes.html"><img src="img/p-pressbox.gif" height=56 width=131 alt="Press Box Shuttle" border=0></a>
</td>
<td align=center valign=middle>
<a href="cmp/jamcentral/jamcentralframes.html"><img src="img/p-jamcentral.gif" height=67 width=55 alt="Jam Central" border=0></a>
</td>
<td align=center valign=top>
<a href="cmp/bball/bballframes.html"><img src="img/p-bball.gif" height=62 width=62 alt="Planet B-Ball" border=0></a>
</td>
<td align=center valign=bottom>
<br>
<br>
<a href="cmp/tunes/tunesframes.html"><img src="img/p-lunartunes.gif" height=77 width=95 alt="Lunar Tunes" border=0></a>
</td>
</tr>
<tr>
<td align=middle valign=top>
<br>
<br>
<a href="cmp/lineup/lineupframes.html"><img src="img/p-lineup.gif" height=52 width=63 alt="The Lineup" border=0></a>
</td>
<td colspan=3 rowspan=2 align=right valign=middle>
<img src="img/p-jamlogo.gif" height=165 width=272 alt="Space Jam" border=0>
</td>
<td align=right valign=bottom>
<a href="cmp/jump/jumpframes.html"><img src="img/p-jump.gif" height=52 width=58 alt="Jump Station" border=0></a>
</td>
</tr>
...
</table>

That’s the first two rows, including the logo. You get the idea. Everything is laid out with align and valign on table cells; rowspans and colspans are used frequently; and there are some <br>s thrown in for good measure, to adjust vertical positioning by one line-height at a time.

Other fantastic artifacts to be found on this page include this header, which contains Apache SSI syntax! This must’ve quietly broken when the site was moved over the years; it’s currently hosted on Amazon S3. You know, Amazon? The bookstore?

1
2
3
4
5
6
7
<table border=0 cellpadding=0 cellspacing=0 width=488 height=60>
<tr>
<td align="center"><!--#include virtual="html.ng/site=spacejam&type=movie&home=no&size=234&page.allowcompete=no"--></td>
<td align="center" width="20"></td>
<td align="center"><!--#include virtual="html.ng/site=spacejam&type=movie&home=no&size=234"--></td>
</tr>
</table>

Okay, let’s check out jam central. I’ve used my browser dev tools to reduce the viewport to 640×480 for the authentic experience (although I’d also have lost some vertical space to the title bar, taskbar, and five or six IE toolbars).

Note the frames: the logo in the top left leads back to the landing page, cleverly saving screen space on repeating all that navigation, and the top right is a fucking ad banner which has been blocked like seven different ways. All three parts are separate pages.

Screenshot of the Space Jam website's 'Jam Central'

Note also the utterly unreadable red text on a textured background, one of the truest hallmarks of 90s web design. “Why not put that block text on a solid background?” you might ask. You imbecile. How would I possibly do that? Only the <body> has a background attribute! I could use <table bgcolor>, but then I’d have to use a solid color, and that would look so boring!

But wait, what is this new navigation widget? How are the links all misaligned like that? Is this yet another table? Well, no, although filling a table with chunks of a sliced-up image wasn’t uncommon. But this is an imagemap, a long-forgotten HTML feature. I’ll just show you the source:

1
2
3
4
5
6
7
8
<img src="img/m-central.jpg" height=301 width=438 border=0 alt="navigation map" usemap="#map"><br>

<map name="map">
<area shape="rect" coords="33,92,178,136" href="prodnotesframes.html" target="_top">
<area shape="rect" coords="244,111,416,152" href="photosframes.html" target="_top">
<area shape="rect" coords="104,138,229,181" href="filmmakersframes.html" target="_top">
<area shape="rect" coords="230,155,334,197" href="trailerframes.html" target="_top">
</map>

I assume this is more or less self-explanatory. The usemap attribute attaches an image map, which is defined as a bunch of clickable areas, beautifully encoded as inscrutable lists of coordinates or something.

And this stuff still works! This is in HTML! You could use it right now!

Let’s look at one more random page here. I’d love to see some photos from the film. (Wait, photos? Did we not know what screenshots were yet?)

Screenshot of the Space Jam website's photos page

Another frameset, but arranged differently this time.

1
<body bgcolor="#7714bf" background="img/bg-jamcentral.gif" text="#ffffff" link="#edb2fc" vlink="#edb2fc" alink="#edb2fc">

They did an important thing here: since they specified a background image (which is opaque), they also specified a background color. Without it, if the background image failed to load, the page would be white text on the default white background, which would be unreadable.

This is still an important thing to keep in mind, by the way. I feel like modern web development tends to assume everything will load, or sees loading as some sort of inconvenience to be worked around.

Anyway, there’s not much to say here. The grid of thumbnails is, of course, done with a table. The thumbnails themselves are 72×43 pixels, and the “full-size, full-color, internet-quality” images are 360×216. Hey, though, they’re only like 16 KB! That’ll only take nine seconds to download.

The regular early days

So that’s where we started. This all sucked, obviously. If you wanted any kind of consistency on more than a handful of pages, your options were very limited.

And then CSS came along, it was a fucking miracle. You could put borders on stuff! You could set colors without having to copy-paste them everywhere! You could just write HTML and another file somewhere else would make it look like the rest of your website, which was important, because we still didn’t understand what “server-side scripting” was or how to use it to make anything more interesting than a hit counter! (I absolutely wrote a hit counter.)

A website even came along to take this principle to the extreme — CSS Zen Garden is still around, and showcases the same HTML being radically transformed into completely different designs by applying different stylesheets.

But we’re not quite there yet. First is CSS 1, made a recommendation in late ‘96.

I don’t know that this has been explicitly state, but CSS was clearly designed to divorce the existing layout and appearance capabilities of HTML away from the markup structure. That’s why even CSS 1 had the float property — it encapsulated the same functionality as the <img align> attribute. The white-space property exposed what <nobr> and <pre> did. That meant you could use a <p> to indicate a paragraph, and have it still mean “this is a paragraph” while styling it however you wanted.

Beyond that, though, there was very little to CSS 1. You could set the font, color, and background of basically anything (though color keywords weren’t even defined yet, and were left up to the implementation!); you could set margins, borders, padding, and width/height; and that was pretty much it. Even the position property didn’t exist yet.

So while CSS 1 was a tremendous help for trivial aesthetics like colors and fonts, it was useless for layout. Everyone picked it up as a really convenient way to keep the theme consistent, but tables remained king for actually laying out your page.

  • alt stylesheets!! when did those come along.
  • reader stylesheets, much much less useful nowadays

  • blink and marquee

css2 not until mid 98

  • browser wars, css included
  • nothing fucking worked, people stuck to tables for arranging things and css for light details
  • quirks mode! introduced by ie4? (list quirks, i love the img in a table cell one)

So, along came CSS, and it was a fucking miracle. Now you just needed to put that color in a single file:

1
2
3
h1 {
    color: navy;
}

And it would apply to every <h1> on every page you included that file in! This changes everything.

This is about where I come in and where my perspective really starts. But please bear in mind that I was like 11, with no idea what I was doing, talking to mostly other 11-year-olds. I’m sure the paid web devs putting together MSN or whatever had a slightly better grasp on things, but honestly — who the hell was going to business websites in 1998? Like, why would you even do that? The web was for random people to make their own little quirky beautiful things.

very, very long sigh …

Anyway, CSS basically killed all those <body> attributes and <font> tags and whatnot overnight. Budding web developers would get very haughty about how much better CSS was, and how you clearly had no idea what you were doing if you still used <font size=+1>. And so began best practice snobbery.

For some span of time — I want to say a couple years, but time passes weirdly when you’re a kid — this was the state of the web. Tables were still used for layout, but CSS was used for, well, style. Colors, sizes, bold, underline. There was even this sick trick you could do with links where they’d only be underlined when the mouse was pointing at them. Incredible!

But there were plenty of things you couldn’t do. Rather a lot, in fact. Here are some that I remember, and the workarounds we were stuck with.

  • You couldn’t, of course, put rounded corners on anything. You had to make four round-corner images yourself and put them in the corners of a 3×3 <table> (!). Or, if you knew the size of the element ahead of time, you could just make a single background image (!!). Of course, Internet Explorer 6 didn’t understand when an image in this newfangled “PNG” format had an 8-bit alpha channel, so you’d have to either use a GIF with jagged edges, or just include the page’s background color in the corner images so they could be opaque (!!!).

  • Speaking of opacity, that didn’t exist at all. The opacity property was new in CSS 3, and IE choked on PNGs, so if you wanted any sort of translucency, the solution was usually: don’t.

  • What the fuck is a “web font”? Your font options are, uh, whatever’s installed on your computer. I’m sure everyone else has the same fonts you do. Everyone’s using Windows XP, right?

    (There was once a time where people could configure their web browser to use fonts of their choosing, and it would actually matter, but those days seem to be long past. Alas, the stack of defaults means any website that doesn’t specify a font would be ugly for most people.)

  • No box shadows, no text shadows.

  • The +, ~, and > CSS combinators didn’t work in IE until 7, so they effectively didn’t exist.

  • XXX

I cannot stress enough that Stack Overflow did not yet exist. This stuff was picked up from various websites about websites, like quirks mode and Eric Meyer’s website.

(Eric Meyer is a CSS pioneer. When his young daughter Rebecca died five years ago, she was uniquely immortalized with a CSS color name, rebeccapurple. That’s how highly the web community thinks of him. Also I have to go cry a bit over that story now.)

XXX complexspiral?

The struggle begins

  • browser wars around the same time

Then, something happened. I don’t know how, or when, or why exactly. But people started to do website layout using CSS, too.

In hindsight, this was clearly absurd, and let me tell you why. CSS in its original form, which we’ll say for convenience is CSS 2.1, was designed like it was for articles — and nothing else. You could play with margins and font sizes and even colors, all you wanted.

But you could not put things next to each other.

Okay, that’s not entirely true, but it is mostly true.

CSS was, as I understand it, designed to extract out all the presentational stuff that people were already doing with HTML. So it inherited a lot of the HTML model, just abstracted away from specific tags. That meant there were really only two layout models. Inline layout was for stuff like <b> and <i>: variations on style within a line of wrapping text. Block layout was for stuff like <p> and <hr>: a vertical stack of elements that each stretched across the full width of the page (or whatever container).

So if you want to have a navigation sidebar on the left side of your website, what do you do?

You can’t use blocks, because those stack vertically. That leaves inline, but… that’s for flowing text, not putting large complex blocks next to each other. CSS had also introduced “absolute positioning” for sticking elements at a precise position on the page, but people had all kinds of different screen sizes and that approach was remarkably inflexible. What does that leave?

Only one option. And it’s only in hindsight that I can truly appreciate how ghastly this was.

You see, the HTML <img> element normally sits in the flow of text, as an inline element. But if you set its align attribute to left or right, the image would jump out of the flow of text and shift to one side of the document, with text flowing around it. And CSS had absorbed this functionality in a general way, as the float property.

CSS floats were only designed for one thing: flowing text around an image, like you might see in a magazine article. That’s it.

But they were also the only way to put two things next to each other. Because if you have two floats in the same place, then one will butt up against the other.

And so it begins.

The float hack era

Now, to be fair, this wasn’t a bad idea. Crafting layouts out of tables was hellish nonsense, and it suffered from the same problems as embedding colors in your HTML: if you ever wanted to make a slight adjustment, you’d have to repeat it on every single page. Surely, the ideal would be for the markup to describe the content and the CSS to describe how to arrange it on screen.

XXX how a float layout actually worked

XXX why it was brittle etc, but the biggest problem was:

The dreadnaught: Internet Explorer 6

released in 2001, right around when css layout concept was taking off. completely ate market share in XXX, over 95%. basically abandoned for the next five years

and we discovered some problems

and then firefox came along and was taking the world by storm so suddenly everyone had to make their carefully-hacked websites work in both ie6 and firefox, and that was proving to be a problem, because ie6 had a lot of fundamental and severe bugs

acid2!!

what were the biggest things in css3? mention those
– no web fonts, so people basically used whatever shipped with windows, which sucked if you weren’t on windows

using <!-- at the start of your stylesheet lmfao
the bad old days

i came in shortly before the long winter of ie6
browser wars
basically came down to just ie6 and a handful of people using like, konqueror on linux. (hi! ps that became webkit)
let me explain ie6 to you

ie6 box model, solved by strictmode? A LOT of stuff was “solved” by strict mode
– margin: auto treated like 0, but you could use text-align on the parent to center
“min-height” bug, no overflow it just makes stuff taller

no inline-block! i remember being very excited for firefox 4(?) for this reason, was a huge refactor by david baron

oh, the cross-browser hacks. oh my god.
parsing hacks: conditional comments, putting a squiggle before css prop name, ie6 can’t parse >
zoom
ie6 filters to fix stuff sometimes

xhtml??

shenanigans: complexspiral, etc

dhtml”, dynamicdrive (oreilly book from 2007) (dynamicdrive was 2000)

pushback against tables, which some folks didn’t really understand
it was a good idea but css was still designed for print, kinda, concerns, so didn’t have a ton of layout options
everything done with floats, the only way to put stuff side by side
“clearfix”, sob. could do it with ::after but i don’t think ie6 supported those

css reset

lot of stuff done in flash because it was easier

started making progress when firefox came out

the miraculous turning point came when ie6 was dead
but it was a slow, agonizing death, no real moment, kind of up to everyone individually
if you were doing web dev for a job, maybe even 2% ie6 traffic was enough to keep support for it
but wow what a nightmare that was

youtube dropped it March 13, 2010, which i think was the first serious big-name shun and imo marked the end

transitions? jquery. that’s it. an era where “modern” websites had everything sliding around with jquery effects, sort of like the linux 3d compiz cube thing

nowadays, css flexbox and grid, holy crap
rounded borders, sure
but also drop shadows, text shadows, transforms, transitions/animations, filters, svg filters

finally a css feature that lets me say what i want and have it happen, rather than try to coax it into happening implicitly”

fun facts:

(Fun fact: HTML email is still basically trapped in this era.)

grand irony: as soon as rounded glossy bubble buttons became easy to do with css, they went out of style! now we’re back to stuff we could’ve done pretty easily in 1996, except for the round avatars i guess

Weekly roundup: Waste not, want not

Post Syndicated from Eevee original https://eev.ee/dev/2019/09/05/weekly-roundup-waste-not-want-not/

Wee bit late, but I’ve been busy.

  • fox flux: I wrote some push physics tests, now that it’s possible to do that. Removed some old obsolete garbage I’ve hated for like a year, hooray. And then I got stuck in a horrible loop of coming up with a new idea for how to do pushing, realizing it won’t work in some case, making a thousand notes, rinse and repeat.

    I can’t even fall back to spriting, because my tablet broke! Argh.

  • doom: I made WasteNot, a ridiculous ZDoom mod that tracks how much ammo/health/armor you lose by grabbing items when you’re close to the max amount you can carry. Also I put some Doom stuff on Itch and the landing page here.

Very exciting week. I spent a lot of it exhausted, after rushing to invert my sleep schedule in not very much time.

Weekly roundup: Breaking up (code) is hard to do

Post Syndicated from Eevee original https://eev.ee/dev/2019/08/28/weekly-roundup-breaking-up-code-is-hard-to-do/

  • irl: I went to the dentist, which I think was the last of the errand backlog, hallelujah.

  • fox flux: Continuing on from last week, I threw myself headfirst into this idea of splitting up base actor code.

    I tried it against Isaac’s Descent HD (my LÖVE port of Isaac’s Descent that I only ever released on Patreon), since it has a very small number of abilities and objects, and just went hog wild.

    The results have been promising! Most of it went much more smoothly than I expected. A little bit was much more horrible than I expected. But within the space of a week I’d gotten a rough first attempt working, ported it to fox flux, and gotten the game… um… mostly limping along. There’s still some lingering fallout, and I haven’t even gotten to Lexy herself yet, but it seems like this will be an overall improvement. I can even write tests now! Tests!

    I also did some more work on the revamped Lexy sprites, but then my tablet broke — again — so that came to a screeching halt.

  • blog: I started on a second post (without finishing the first, hm), or more specifically, I started on a complicated but very cool interactive doohickey to accompany the second post. Very excited. Should probably, like, finish one of them.

  • alice: Planning, writing.

Weekly roundup: Chugging

Post Syndicated from Eevee original https://eev.ee/dev/2019/08/19/weekly-roundup-chugging/

I’m discovering all kinds of ancient damage in myself, but I’m chugging along!

  • irl: We’re making our way through an endless backlog of errands. There are so many. But they’re getting done, which is good! Also the internet was out for a day so that was fun.

  • gleam: It got a big ol’ refactor, which left it working exactly the same, so that’s fun! Stubbed out enough features that it’s now (technically…) possible to use it to make a VN from scratch, rather than just previewing and making minor edits to an existing one. I split apart the player from the editor and ensured the player works standalone; I added a loading screen; and I finally got around to adding back music support. It’s coming along!

  • art: I did like half a dozen daily comics? You know. “Daily”. The last one was… oops, almost a week ago. I’m sure I’ll get back to them real soon now.

    Also some sketching! I’ve almost filled a real physical sketchbook for only the second time in my life.

  • stream: I took a crack at Sigil on UV, trying for 100% kills and secrets, with… mixed results! Great fun, I guess.

  • fox flux: I am admittedly struggling a bit.

    We played Cadence of Hyrule; I found the art style inspiring; I tried to glean something from it that I could apply to my sprite work; and I realized basically everything I’ve drawn is counter to what I most like in pixel art. It was a struggle just to produce the few tiles I have so far, so I don’t know what to feel or do here.

    At Ash’s suggestion, I started trying to draw some Dewclaw tiles, but boy! That’s difficult. How do you design small pieces that can be put together into something sufficiently reminiscent of a city? I don’t even know how to draw a city, not really; I’m remarkably terrible at filling in small details of a concrete place or situation.

    And then I tried to do something technical and split up Lexy’s code — since historically it’s been littered with a ton of if self.form == 'foo' then ... special cases — only to discover that it breaks everything. Now I’m trying a different approach, which is not breaking everything quite as badly, but which has massive repercussions and possibly slows the game down by double-digit percent. Love game development.

  • alice: Still plodding along on Alice’s Day Off. I wrote a half-draft, half-outline of another route. Just been hard to get in the right mood, lately.

  • blog: I started on a post! Wow! Remember when I used to write posts? I’d like to do that again. I’ve got one half-done and ideas for a few more, if I can just get some momentum going again.

I’ll get there.

Weekly roundup: GLEAM

Post Syndicated from Eevee original https://eev.ee/dev/2019/08/04/weekly-roundup-gleam/

Hello. I don’t know how I am! But I did some stuff.

  • fox flux: Workin’ on a new walk animation.

  • gleam: After years of saying I should totally do so, I finally started making a little editor for the Floraverse web VN engine. I’ve been gradually teaching it to load and play back the existing VNs (from scratch, because the old code is Quite Bad), and it’s finally hitting the point where it’s possible to make something from scratch. Sort of. I mean, there’s no saving or loading or exporting, and a bunch of stuff is broken, but you know. Getting there. Maybe I’ll even make a VN myself.

  • art: I started doing daily comics again and then forgot after day 1.

GLEAM has basically taken up my whole week; turns out that while client-side web stuff has improved dramatically, writing an editor is still an incredible pain in the ass. Getting somewhere, though.

Oh, and that marks the end of my journal! Cool, I guess. I don’t tend to fill up notebooks very often.

Weekly roundup: Recharging

Post Syndicated from Eevee original https://eev.ee/dev/2019/07/28/weekly-roundup-recharging/

Hello. I’m kinda up and down but recovering, I think.

  • art: I drew a bunch of porn, most of which is on my porn gallery (warning: porn). I even wrote some stuff, which will never see the light of day.

    I also finished putting all my 2015 art on my clean gallery, if you want to see the arc of my art journey, which slowed considerably after the first couple years. Kinda bummed about that.

  • irl: We have done so many fucking errands you have no idea.

  • gleam: I put together another Floraverse VN, but more importantly (to me anyway?), I’ve actually made some inroads on making a little editor for these things. It’s not entirely functional yet — did you know that drag-and-drop is a huge pain in the ass — but it resembles something and I’m making swift progress. Hallelujah.

  • fox flux: I gathered up like a dozen pages of dense notes and kinda consolidated them into one place, which is nice.

    I also, accidentally, uh, okay funny story, I was taking notes on paper and I doodled Lexy pulling a lever, and later I tried to sprite it based on her current sprite, and I didn’t like it a lot, so I pixel-traced over the drawing instead, and it was way better, and this led me on a journey that ended up with a completely different sprite design. It’s a thousand times better in every possible way, but I’ve also invented a massive pile of work for myself, because now I have to redesign a dozen variants of her and redraw like 200 sprite frames. It kind of feels like I’m back to square one and have accomplished nothing at all on this game, in fact! But fuck me it’s so much better

Next week marks a fun milestone. I’m now on the very last page of the book I’ve been using to jot this stuff down, one week per page. It spans almost four years. I should probably find another one real quick.

Weekly roundup: Vacay

Post Syndicated from Eevee original https://eev.ee/dev/2019/07/16/weekly-roundup-vacay/

I’m burnt out. I just can’t get into anything. And I’ve been dealing with a huge stack of accumulated errands from last month. And it’s fucking hot in here and that just pisses me off all the time???

So I’m trying to step back and chill and draw and hang out with folks and whatever. Sorry. I don’t know why I’m apologizing.

  • fox flux: Added some sparkles to a key.

  • mario maker: Made Star Anise’s Dream Land (5TQJG0MNG), a happy-go-lucky level inspired by my cat, and Koopa Valley (463-9CJPVG), an attempt at some standard friendly SMW-like fare. Also made half of like six other levels, but I’m having trouble even finishing those.

  • art: I’ve been drawing, just, a bunch of porn. It’s nice to be getting back into that. Drawing, I mean, not porn. But porn too.

See you next week.

Weekly roundup: Let’s try that again

Post Syndicated from Eevee original https://eev.ee/dev/2019/07/02/weekly-roundup-lets-try-that-again/

Hello, hello! It’s been a while. June ended up being an avalanche of errands and personal problems that neatly segued into each other, over and over. Good times! I think everything’s settled down now, but who knows.

Anyway, that gives us three weeks to catch up on:

  • fox flux: Finished and committed a bunch of half-implemented ideas in an attempt to get git clean for once (still more to go though); took a crack at porting sound effects from MilkyTracker and sfxr to Sunvox, which was much harder than expected; experimented with a nighttime palette; drew some new vastly improved swimming sprites from scratch.

    Did some work on the camera, which has always been pretty lazy. (I’ve improved it a lot since that recording, so don’t judge it too harshly.) Started on a redone menu, which should be a great improvement over the demo’s menu which was just “resume” and “quit”. Redrew the base dialogue portraits, and they look fantastic, but apparently I never tweeted about that, but you can see it in the next link!

    After spending all this time on miscellaneous mechanics and other bits and pieces, I decided it was finally time to get a basic gameplay loop going — enter a level, get some stuff, leave the level. The results are extremely rough, but I’ve made a start! It’s turning into a game! Which is weird because it was already a game once!

  • secret game engine thing: Not a lot, but I’ve cleared some design roadblocks that were seriously getting in the way.

  • art: Some doodles. Also I drew some beautiful gift art for my and Ash’s Metapodth anniversary.

  • alice’s day off: Wrote some stuff! It’s a miracle.

Currently attempting to get my ass back in gear, with moderate success.

Weekly roundup: Ironically stable

Post Syndicated from Eevee original https://eev.ee/dev/2019/06/12/weekly-roundup-ironically-stable/

I remain on a fox flux kick. Keep trying to do other stuff as well and then not doing that? Hm.

  • fox flux: Documented the hell out of all my rewritten collision code, removed some old hacks, put some methods on a new type that was an ad-hoc table before, and fixed a final remaining edge case in a satisfying way. Did kinda start writing about all this but didn’t finish it yet.

    Then I fixed all the stuff I’d broken about pushing in the process, and cleaned it up somewhat.

    Water is gradually improving but still kinda rough.

    Also added some experimental candy? Candy is pretty good.

    I did some more overhauling of the palette; I’m really really liking how it’s coming out.

    And also a preposterous amount of brainstorming. Like I’ve got half a dozen sheets of paper with tiny 8pt notes crammed on them. This ought to be a fun game.

Welp, back to that, then.

Weekly roundup: Exactly at the top

Post Syndicated from Eevee original https://eev.ee/dev/2019/06/05/weekly-roundup-exactly-at-the-top/

Hello! I’ve been a little preoccupied with meatspace things again, but here is some digital stuff.

  • fox flux: I have been a busy little beaver. I consolidated 1D and 2D motion, made ground adherence more conservative about how far it tries to drop you, and totally overhauled climbing to not incredibly suck. But who cares about any of that.

    What I really did is spend like a solid week overhauling collision detection. Finally, after years of wanting it, I have overlap resolution and nearly zero-cost contact detection! Which means that if objects overlap by some horrible twist of fate, instead of freely clipping through each other, they’re now free to move apart but not closer together. It’s god damn magic. Also I now know exactly where you’re touching objects which will probably come in handy for like, critters that walk back and forth on a platform without walking off it? Or something? I forget exactly why I wanted that but hey it’s nice.

    As an added bonus, I can finally fix climbing off the top of ladders — instead of hopping off the top and then landing, you stop at exactly the top, which is incredibly satisfying.

    I will almost certainly be wringing a blog post out of all this.

  • art: I worked more on that animation and then kinda forgot about it. Hm. Also some doodling or whatever?

    I drew a little… comic? Series of panels? I drew a thing about a ground adherence bug I ran into, and also a general explanation of ground adherence. It’s on Twitter, though it seems worth preserving elsewhere, once I figure out where that is.

  • gleam: I finally made some kind of real start on an editor for the little Flora VNs I put together. It doesn’t do a lot yet, but it has some UI, which is backwards from how I usually make these things, so that’s promising.

  • stream: Ash streamed some Spyro while I commentated, and then I streamed some Hat in Time while they commentated, and that was all great.

I am juggling too many things but I extremely want to get them all moving so I guess I’ll get back to it!

Weekly roundup: Pushing it

Post Syndicated from Eevee original https://eev.ee/dev/2019/05/15/weekly-roundup-pushing-it/

I remember saying something about balancing my time better, and that did not happen.

  • fox flux: I basically spent the whole week working on push physics. It was tough going at first, but I finally got it working correctly which feels like a goddamn Christmas miracle.

    I probably did some sprite work in there somewhere too, to let my brain cool down a bit.

    I’m excited about this game, ah! There’s a ton of work to go but I’m actually starting to see some mechanics come together.

  • stream: Ash and I played a ridiculous adventure game for a bit. Hm, maybe we should finish that. It’ll be on YouTube, uh, eventually.

Not so much this week; I ended up nocturnal and that threw me entirely for a loop. Back to waking with the sun now and feeling pretty good, so, fingers crossed.

Weekly roundup: In flux 2

Post Syndicated from Eevee original https://eev.ee/dev/2019/05/05/weekly-roundup-in-flux-2/

  • fox flux: I’m not sure what happened but I mostly did fox flux this week! It’s kind of a huge mess at the moment — I have a thousand lines of uncommitted changes from a dozen different half-finished experimental ideas, which makes starting on a new idea a bit daunting. So I spent some time finishing up and committing about half of that stuff, and then… um… started a few new half-finished experimental ideas. I am good at software development.

    I got a bit lost in the weeds trying to make the physics of pushing blocks work a bit better, which I’d still like to do, but I think it might require completely rethinking how pushing works (mainly in order to avoid a two-pixel gap in some situations, sigh, but that kinda thing’s important to me) and also redoing how friction and whatnot works. I can’t wait.

    Also been finishing up some visual effects I started ages ago but didn’t quite figure out, filling in some missing pixel art (which I think I got a little better + faster at), and fleshing out mechanics + trying out some new ones. It turns out, if you think your game needs more mechanics, a good place to start is to implement the existing ones so you can run around and play with them freely and see what new stuff comes to mind. Who knew?

  • art: I painted a picture. Not porn, for once! I’m definitely gonna do this more often; it was quicker and easier than I expected, and came out better too.

I missed working on fox flux and am glad to be doing it again, but I’ve clearly gotta balance my time across other stuff a bit better, too.

Weekly roundup: Bit of this, bit of that

Post Syndicated from Eevee original https://eev.ee/dev/2019/04/29/weekly-roundup-bit-of-this-bit-of-that/

I don’t have a cool theme or pun this week!

  • irl: I did a whole bunch of errands, aggressively slashed my tab/email count, and went hiking. Very exciting for you, I know.

  • secret thing: I taught it to animate tiles and movement, and tried this out with a conveyor belt, which instantly threw a wrench in my whole plan. Hm, well, I’ll figure it out. I wrote about the concept for $4 patrons (who will also be getting a bunch of beta builds when this is usable), if you’re interested.

  • cherry kisses: I have like four logic bugs reported by several different people, and they all feel related, but they’re also completely impossible. Like there is no way any of these could’ve happened. Except they did. And I have no goddamn idea how. I’ve spent like a day and a half wrestling with this and have barely made progress so far, but I would really like to make the game not randomly crash for folks.

    At least it autosaves, I guess.

  • art: I drew more things and I increasingly like them! I don’t know what happened, but I hit a point where I’m aggressively attacking all kinds of small details that I don’t do quite right — details that, formerly, I’d just glaze over because it was hard enough getting the general pose right. So that’s good.

    Still working on categorizing old SFW art, too. There’s just a whole lot of it.

  • fox flux: I picked this back up, but didn’t actually make tangible progress until Sunday, but I’m listing it anyway to pad this list out a bit.

  • streaming: Ash played through Doom II totally blind, while I provided commentary, which I guess doesn’t make it totally blind. Anyway we have a whole playlist of this nonsense now.

I’m juggling half a dozen things and am generally excited about all of them! It’s a nice way to feel.

Weekly roundup: Back to normal

Post Syndicated from Eevee original https://eev.ee/dev/2019/04/22/weekly-roundup-back-to-normal/

As I said before, I was occupied for a bit, but now I should finally be able to get back to doing these weekly! I did manage to get a few things done over the past three weeks:

  • flora: Finished up and published a Luneko species sheet! Happy April Fool.

    That’s Anise. Anise is the April fool, and also he’s happy.

  • blog: I wrote about how the particle wipe generator works, in lurid detail! I think it’s an interesting little read, even if you have no use for the tool itself.

    I also spent a lot of time backfilling old art on my (clean) art gallery. It’s not updated quite yet; there’s a lot to go to, shockingly so, and I haven’t even made it through year one yet. Honestly, I’m kind of embarrassed by how much my output declined over time.

  • art: Speaking of, I’m back to drawing regularly, instead of just saying I wish I were drawing regularly! I think I’ve actually been drawing pretty regularly for like two weeks now. Most of it is porn. I should probably draw some not-porn, too. It’s just, you know, porn is a lot of fun to draw.

  • secret thing: I laid some groundwork for the little game engine I’m writing and haven’t really talked about yet. More on that, including maybe even a name, once I feel like I have some kinda proof of concept.

  • sudoku thing: I taught it about extra regions so now it can be used to play hyper sudoku? I don’t know why I’m even making this. It’s kind of unusable until I add undo/redo and puzzle generation, and both of those are effort. I guess I’ll see if my spite is strong enough to power me through both.

  • streaming: Ash and I played video games on the internet while high and you can watch it if you really want to for some reason.

Hey, that’s not too bad a haul, considering I didn’t even have time to work for most of the month! Got some good stuff going on, glad to see I’m up to speed again at last.

Particle wipe generator

Post Syndicated from Eevee original https://eev.ee/release/2019/04/20/particle-wipe-generator/

Animation of solid orange transitioning to green via a swirl of little fox face shapes

🔗 Particle wipe generator on itch or hosted locally
🔗 Source code

This is a tool for making particle wipes, a type of transition whose name I made up because I don’t think they have a well-known name! They can be used in Ren’Py, RPG Maker, or anything that lets you write a shader.

Most of my games have done screen transitions with simple fades, and I wanted to try something different here, but I couldn’t find a tool to make the effect I wanted. So I wrote my own. If you’re interested, here’s how it works:

The idea

I was inspired by two things. One is Cave Story’s transitions.

The end of Cave Story's intro cutscene, which transitions to gameplay with an animated pattern of diamonds

That looks rad, right? I think it does, anyway. I wanted to do something similar myself.

At a glance, this effect looks pretty simple. The screen is sliced into a grid. A diamond shape starts expanding from the center of each cell until the cell is filled. By staggering when each cell starts, you can make an animation that seems to wipe from the bottom upwards, or from the edges inwards, or who knows what else.

Here’s a frame from the above capture, showing the grid. You can see from the blocks near the middle that it’s the same as the tile grid.

Notice that Cave Story transitions either from a scene to a solid color, or vice versa. Offhand, I don’t think the game ever transitions directly between two scenes.

My guess is that it’s manually drawing solid color on top of the tilemap until the entire screen is obscured, switching maps in the background, then reversing the progress. The various sizes of diamond might even be physical sprites on a foreground layer!

That poses a slight problem for me, because I want to be able to transition directly between scenes as well. Enter inspiration number two: Ren’Py.

Ren’Py is a visual novel engine, and it supports a ton of screen transitions. That makes sense, since visual novels generally don’t have much animated art, so most of the animation happens in transitions and sprite effects.

One such transition is a generic one called ImageDissolve, which can do a mask transition (another term I made up). It takes a grayscale mask, which tells it the order to reveal pixels. Where the mask is black, the corresponding pixels of the “after” scene are shown almost immediately; where the mask is white, those “after” pixels are the last to appear.

(I suddenly realize that Ren’Py does that backwards, with white pixels being first, but that doesn’t make sense to be since black pixels are zero.)

That’s a bit of a mouthful to describe with text, so here’s a basic example. A linear gradient from black to white will play out as a straight wipe in the same direction.

This approach can capture any kind of transition where pixels are revealed in a given order, and if I implement it with a shader (which is very easy), I can emulate the Cave Story style without being limited to a solid color! Neat!

The problem

The problem is… how do I generate the mask image? I searched around a bit and found folks who’d made transitions for use with Ren’Py, but no explanation of how they did it.

Let me think about emulating Cave Story’s effect using the mask approach, one step at a time.

Forget about the wipe effect for now and concentrate on a single cell. When the diamond is just starting to appear, it should be black. When it completely fills the cell — i.e., when it’s big enough that its edges just barely touch the cell corners — it should be white. In the middle somewhere, it should be medium gray. Imagining (or drawing) a few cases suggests a simple diamond gradient, which seems correct.

Now for the wipe effect. All it really does is stagger when the animation starts. The upwards wipe, for example, starts animating all the cells on the bottom row, waits some short amount of time, then starts animating all the cells on the next row up, and so on. An ASCII diagram of this process (for a simplified, smaller screen) might look like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
   00 |                     XXXXXXXX
   01 |                  XXXXXXXX
R  02 |               XXXXXXXX
o  03 |            XXXXXXXX
w  04 |         XXXXXXXX
   05 |      XXXXXXXX
   06 |   XXXXXXXX
   07 |XXXXXXXX
      +-----------------------------
                  Time

My example cell above spans the full range from black to white, but if I want to stagger the cells like this, I need to squash that into a smaller range. What range, though? To get that scaling right, I need to know the total time the entire animation takes.

That’s kind of a weird question, because nothing I’m working with actually measures time! I only have numbers from 0 to 1; the amount of time is really a matter of how fast you play back the animation.

So let me approach this the other way around. The total “time” is 1, the full range of values I’m working with. My example has 8 rows. Each row starts playing after the row beneath it is ⅜ of the way through its animation; call this fraction the “delay”. There are 7 such delays, one fewer than the number of rows, because the first row doesn’t have a delay.

If the length of a single cell’s animation is \(t\) (which is actually a fraction of the length of the whole animation), then the last row starts after a total delay of \(t \times (8 – 1) \times \frac38\). Its own length is \(t\), and that should bring us to the end of the animation, so:

$$
\begin{align*}
1 &= t \times (8 – 1) \times \frac38 + t \\
&= t \times (7 \times \frac38 + 1) \\
&= t \times \frac{29}{8} \\
\Rightarrow t &= \frac{8}{29} \approx 0.276 \\
\end{align*}
$$

And indeed, if you count characters in the diagram, each bar is 8 long out of a total width of 29. Neat! All I have to do is make the bottom cells range from 0 to 0.276, the next row up range from 0.103 to 0.379 (the same size range, but moved up by \(\frac{8}{29} \times \frac38 = \frac{3}{29}\)), and so on.

Easy. Blog post done.

Except…

I wanted to use hearts. And hearts create two new issues.

The first is that I don’t know how big a heart would have to grow to cover the entire cell. For diamonds, that was easy: they’re symmetrical in the same way as squares, so it’s obvious that they just need to be big enough to touch the corners. But how big does a heart have to be to fit a square entirely inside it? Do I gauge it by hand in an image editor, or what?

The real problem there is that a heart is, presumably, a bitmap rather than a simple shape with properties I can examine mathematically. And even if it were a shape, the math would get pretty ugly pretty quickly.

But the second problem is worse. Hearts aren’t vertically symmetrical, which means a neighboring heart might poke into a cell and start covering pixels that the native heart hasn’t covered yet.

A 3×3 grid of hearts expanding out of their cells, showing that the top of a heart can grow into the cell above.

This complicates things considerably. If I took the naïve approach of gluing together a bunch of independent cells, then the top of each heart would reach the top of its cell and flatten out into a hard border! Sounds ugly, especially since the grid isn’t really supposed to be visible in the animation. (Technically this could happen with diamonds too, if the delay were high enough, but their symmetry makes it much harder to notice.)

Now, I could fudge my way through both of these problems with sufficient abuse of an imaging library. Draw a very tiny black heart, then draw a slightly bigger almost-black heart, and keep expanding until every pixel has a color, then either scale the colors or go back and do it again knowing the correct range.

But that’s not a very satisfying solution, and it’s not very precise — which is important when I only have 256 values to work with. I can do better!

Doing better

The approach I used for diamonds above is fairly promising. It’s most of the way to a blueprint for figuring out exactly what shade each pixel of the mask should be, independently of any other pixel. The position within a cell tells me how far along in the cell’s animation the pixel is (center black, corners white, everything else somewhere in the middle), and the delay tells me how to scale that to fit correctly in the full animation.

Those seem like reasonable steps. All I have to do is fix them to work with an arbitrary “particle” shape. Somehow.

Step 1: the stamp

Forgetting about the overall animation worked before, so I’ll do it again and concentrate on a single prototype cell. That cell will be repeated (with some adjustment) all over the final mask, so I call it a stamp.

I already know in advance that a single cell doesn’t need to scale from 0 to 1, since I’ll be adjusting it later anyway, so that frees me up to use any arbitrary quantity — as long as it’s scaled by some consistent factor I can eliminate later. A little thinking suggests that what I really want to know is: given a pixel \((x, y)\) within a cell, how big does the heart particle have to grow to hit that pixel? I can express that as a fraction of the particle’s original size (since it should grow proportionally), and then worry about scaling it down later.

The first thing I want to do is change my coordinate system. Consider: for a 10×10 cell, the center is at the point (5, 5), which neighbors pixels (4, 4) and (5, 5). But that would mean the heart would touch the pixel at (5, 5) immediately, whereas it would need to cross a whole pixel to reach (4, 4), even though both pixels touch the center!

Close zoom of the problem described above, with the top-left corners of pixels in red, and their centers in blue

Pixel coordinates refer to the top left corners of the pixels, indicated in red above. The center is a point, not a pixel, and it’s clearly much closer to one pixel coordinate than the other. The fix is to use the centers of pixels, indicated in blue, which are the same distance from where the heart starts. Phew!

(If you don’t do this, you’ll get a very noticeable diagonal gash where the particle touched lower-right pixels earlier than upper-left ones. Guess how I found that out!)

While I’m at it, pixel coordinates are relative to the upper-left corner of the cell, but the most interesting point here is the center. So let’s make them relative to that, too. That means (4, 4) and (5, 5) should really be (-½, -½) and (½, ½), or more generally: given a center at \((c_x, c_y)\), the point I’m actually interested in is \((x + \frac{1}{2} – c_x, y + \frac{1}{2} – c_y)\). Call this, I dunno, \((d_x, d_y)\).

Back to the actual problem, which is: how big does the particle need to grow to hit this point?

Like I said before, I could try scaling the particle up bit by bit (maybe binary search?) until it touches the point, but that still feels goofy and imprecise.

You know, it sucks that the particle is a two-dimensional shape. It would be swell if I could eliminate a dimension here, or something.

And here I borrow a couple techniques from collision detection. Scaling the particle up is equivalent to scaling the entire cell down. If I scaled the cell down towards the origin, the point would trace a straight line.

Animation of a grid scaling down towards the origin, showing that a point traces a straight line

This is very helpful. It means I can solve this problem with a raycast: fire a straight ray into the particle, towards its center, and check each pixel it hits until I find an opaque one. That’ll give me a perfect answer!

But where does the ray start? I have a point in the grid, but not a point on the particle. So the first question is: if the particle scaled up just enough that the edge of the particle image touched the point, where on the particle would that contact be?

The same point as before, but with the particle grown to barely touch it

Call the particle dimensions \(p_w\) by \(p_h\). (My heart is contained within a square, but that isn’t strictly necessary.) In order to reach x-coordinate \(d_x\), the particle would have to be twice as wide as the distance from the y-axis to that point — because it’s centered! — which is \(\left|2 d_x\right|\) pixels wide. Its scale, relative to its original size, would thus be \(\frac{\left|2 d_x\right|}{p_w}\). The scale for touching the y-coordinate would be computed the same way. To actually touch the point, the particle has to reach whichever coordinate is further away, so its scale must be:

$$
s = \max\left(\frac{\left|2 d_x\right|}{p_w}, \frac{\left|2 d_y\right|}{p_h}\right)
$$

A special case crops up here: for a cell with an odd width and height, the center pixel is exactly aligned with the origin, and the scale computes to zero. I’m doing some division in a moment, so that’s very bad — but the center pixel is effectively touched immediately, so I can say the final answer for this pixel is 0 and skip the rest of this anyway.

Now for the fun part! When the expanding particle hits the point of interest, it makes contact at some point on the original particle image. If the necessary scale is \(s\), the contact point is the center of the particle, offset by \(\left(\frac{d_x}{s}, \frac{d_y}{s}\right)\).

And now I raycast from that point to the center of the particle and check every pixel that ray crosses, using a modified Bresenham’s algorithm — originally intended for drawing pixel-perfect lines, but perfectly suited for casting a ray through a grid as well. (Conveniently, I’d already implemented this sort of raycast for collision detection for this very same game! Then I ended up not using it, hm.)

When I find an opaque-ish pixel (alpha of 0.5 or greater), I compute its distance from the center, divide by the distance from the contact point to the center — that tells me how much bigger the particle has to grow for the opaque-ish pixel I found to actually touch the point.

Multiply that ratio by the \(s\) I found earlier, and the result is exactly what I was looking for: the scale of the particle when it touches the point!

Now, raycasting for every pixel in the stamp — a thousand times even for a dinky cell size of 32×32 — is not exactly speedy. But it’s not unbearably slow, either. And this is something that’s generated once and played back a bunch of times, so why not spend a little CPU time upfront making it as high-quality as I can manage?

Anyway, that’s the hard part done! Now I can put the mask together.

Phase 2: the mask

With the stamp generated, I also know how big the particle has to grow for the entire cell to be covered: it’s just the highest scale in the stamp. For a simple full-screen effect, all I’d have to do at this point is scale the stamp values into the range [0, 1] and copy them to every cell in the grid.

But that’s boring; I wanted a wipe, which requires a couple more twiddles.

The wipe is essentially a second animation that controls when each cell’s individual animation starts. Above I considered a row-by-row wipe; for Cherry Kisses I ended up with a column-by-column wipe; Cave Story also has an “inwards” wipe. All of these can be generalized as numbered steps in a grid:

1
2
3
4
5
6
7
8
9
By row      By column   Inwards
77777777    76543210    01233210
66666666    76543210    12344321
55555555    76543210    23455432
44444444    76543210    34566543
33333333    76543210    34566543
22222222    76543210    23455432
11111111    76543210    12344321
00000000    76543210    01233210

The math is basically already done; I did it above. Given the number of steps \(n\) and the delay \(d\) (a fraction of the cell animation time), I can find the length of a cell animation \(t\) as follows:

$$
\begin{align*}
1 &= t \times (n – 1) \times d + t \\
&= t \times ((n – 1) \times d + 1) \\
\Rightarrow t &= \frac{1}{n d – d + 1}
\end{align*}
$$

The process for generating the whole mask is thus:

  1. Iterate over each pixel of the mask.
  2. Figure out what cell it’s in, and the step for that cell.
  3. Find the corresponding value in the stamp, scale it to the size of a cell animation, and add in the delay.
  4. Write that to the mask.

Once every pixel is done, the mask is complete!

Except… this didn’t handle the overlap issue. No problem, though; that’s surprisingly simple to fix.

First, expand the stamp to the size of a 3×3 block of cells. The maximum scale for a stamp should only be taken from the central cell; the others are for the following process.

Then, when reading a pixel’s stamp in step 3 above, read it from the central cell — and also from the neighboring cells. In those neighbors, I read from the stamp cell on the opposite side, in order to know how long it would take for the heart to grow out of that cell and into this one.

(Diagonal neighbors aren’t shown here, but you get the idea.)

Since different cells may have different start times, I may need to add/subtract some extra delay from the neighbors’ values. Then I take the smallest of all these samples to figure out the earliest time that any heart — either in this cell, or one of its neighbors — hits the pixel.

And hey, presto, we’re done! Here’s a (somewhat laggy) recording I took of the very first time I got this working for Cherry Kisses:

It ended up a little nicer-looking than that, of course. (Feel free to play the game to see it in action?) And if you’re curious, here’s the mask from the final game:

Caveats

There’s one teeny tiny problem still lingering in this approach. I assume that the whole animation ends when the last cell animation ends, but because of cell overlap, it might actually end earlier. And indeed, when I went back to check the Cherry Kisses mask, I found that it ends early — the brightest color in it is #e2e2e2. Oops. So much for that ludicrous accuracy!

That’s still fixable by taking overlap into account when finding the maximum value in the stamp, but I haven’t done it yet, and it’s a bit more complicated if the grid pattern has adjacent cells that are more than 1 step apart. (Those cases can also lead to particles mashing against the cell edge too early, which could be fixed by using a 5×5 or larger stamp…)

That’s it

Yep. The particle generator is just this logic with some knobs bolted on. It has a couple extra features, like a “halo” that highlights the transition point, and using all three color channels for extra precision, but it’s all built on the same basic idea.

There’s a lot of room for experimentation and variety here, and I’ve probably only scratched the surface. This is only a tiny subset of what can be done with a transition mask, too — it needn’t rely on a grid at all! See what you can come up with.

Oh, and here’s the exact shader I used in Cherry Kisses. It’s for LÖVE, so it has a couple non-standard #defines and globals, but you get the idea. The “ramp” is just a tolerance that adds a soft edge around the transition.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
extern Image mask;
extern float t;
extern float ramp;

vec4 effect(vec4 color, Image texture, vec2 tex_coords, vec2 screen_coords) {
    vec4 pixel = Texel(texture, tex_coords) * color;
    float discriminator = Texel(mask, screen_coords / love_ScreenSize.xy).r;
    float alpha = clamp((t - discriminator) / ramp + 0.5, 0.0, 1.0);
    pixel.a *= alpha;
    return pixel;
}

Happy transitioning!