\n\n\n\n
I first remember André Braugher from his performance in Glory where he played perhaps—low key—the most important role in the movie. He played the person with the most to lose and the least to gain by joining the army and fighting to end slavery (something the movie later acknowledges is pretty much a fool's errand). He plays the person we—the viewer comfortably separated from the events portrayed by circumstances, time, and knowledge of what will happen—should be but almost certainly won't be. (No more details: watch the movie if you haven't seen it.)
\n\n\n\nMost people will know him either from his role in Brooklyn Nine-Nine, an above average a great sitcom of recent years, or Homicide: Life on the Street, the best police procedural ever made, based on a fantastic non-fiction book by David Simon. (I revised this paragraph after conferring with numerous colleagues and discovering that my daughters' opinion is widely held; I am outvoted!)
In Homicide he is again playing someone who stands for justice despite his own self-interest. He is the black man with obvious intellect and education who chooses to work as a Homicide detective when there are so many better options for him, it ruins his marriage, and it is killing him. He works within a corrupt and under-resourced system and with colleagues he pretty much despises trying to make the tiniest difference when and where he can, and usually to his own disadvantage.
\n\n\n\nAnd, despite its being a comedy, as Raymond Holt in Brooklyn Nine-Nine he somehow again plays someone pretty much in this situation except that, now an older man and a captain, he has somehow navigated an earlier phase of life in which all of… this… was much worse, and today is comfortable enough that the horribleness is purely and not always darkly comic.
\n\n\n\nHomicide is one of my favorite TV shows of all time. Brooklyn Nine-Nine is my daughter's favorite TV show of all time.
\n\n\n\nAndré Braugher is already missed.
\n","$updatedAt":"2024-06-05T09:10:30.273+00:00",path:"andr-braugher-rip",_created:"2024-07-09T20:28:30.241Z",id:"8106",_modified:"2024-07-09T20:28:30.241Z","$id":"8106",_path:"post/8106"},{date:"2023-12-02T14:24:54.000+00:00",summary:"What vector graphic editing software produces clean output with minimal control points, specifically for creating symmetrical shapes and performing boolean operations, as explored by the author who has experience with multiple applications including Affinity Designer 2, Sketch, Vectornator, Graphic, Inkscape and Adobe Illustrator?",keywords:["inkscape","svg","boolean operations","vector graphics","sketch","affinity designer","vectornator","graphic","babylonjs","xinjs"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:30.285+00:00","$databaseId":"665dfafe000014726b3d",title:"Pi 5 follow-up","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nI don't have a working version of Illustrator any more, but I strongly suspect Illustrator produces perfect output in this case. In the end, I hand edited the bezier curves in my logo, but it looks like I possibly could have saved time by using Inkscape to do the booleans (and then going back to Sketch to produce clean output).
\n","$updatedAt":"2024-06-05T09:10:30.285+00:00",path:"pi-5-follow-up",_created:"2024-07-09T20:28:30.928Z",id:"8088",_modified:"2024-07-09T20:28:30.928Z","$id":"8088",_path:"post/8088"},{date:"2023-12-01T23:40:30.000+00:00",summary:"What is my experience with setting up and using the Raspberry Pi 5 compared to the Meta Quest 3?",keywords:["raspberry pi 5","quest 3","nodejs","nwjs","chromium","babylon3d","electron","mathml","svg","raspberry pi"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:30.273+00:00","$databaseId":"665dfafe000014726b3d",title:"Pi 5","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nOne of the things I did early during the COVID shutdown was buy myself a Raspberry Pi 400 (the one built into a keyboard) along with the camera module and some lenses. I did not realize that the Pi 400 did not have the required hardware interface to work with the camera (if I recall, the 8GB Pi 4 was already sold out, because a lot of people decided to play with Raspberry Pi devices during the lockdown).
\n\n\n\nAnyway, I never got to play with the camera module and in any event I think I lost track of it during my move to Finland. Maybe it will show up.
\n\n\n\nThe Pi 4 was pretty much perpetually out of stock ever since, with scalpers reselling the device for steep markups on Amazon. But, the Pi 5 seems to be easy to get, at least for the moment. As I type this, my microSD image is being verified…
\n\n\n\nWhen I got my previous Raspberry Pi, I was working at Google which means I was spending a lot of time using Linux, so messing around with the Pi was fun and easy. I got b8rjs working on it and played around. I've since tested xinjs on my old Raspberry Pi, and even found a bug (if I recall correctly, I assumed browsers supported MathML and the Pi's browser does not).
\n\n\n\nFirst thing, I received the Raspberry Pi 5 kit in a ridiculously large, nearly empty box that was mostly full of padding paper. Next, it was hard to open the white cardboard boxes without tearing them, so I just gave up.
\n\n\n\nThe case doesn't include screws (which it seems designed for) or instructions, so I googled the instructions and they were a bit poor (e.g. they told me to make sure the fan was plugged into the socket marked \"FAN\" rather than providing a diagram (it's not in the obvious place and it comes with a piece of plastic blocking it, so it doesn't look like a socket. Luckily I had a set of tools for mucking around with computers that includes a good set of tweezers.
\n\n\n\nAnyway, it assembles very easily (I think I slightly misaligned the heat sink… oh well).
\n\n\n\nFirst nice surprise is that the keyboard is actually, like old wired Mac keyboards, a USB hub. And in fact it one ups Apple by providing three extra USB sockets (although it loses points for having a mini-or-micro-USB socket vs. a type-C socket for the cable coming from the computer. Is that even allowed in the EU these days?
\n\n\n\nThe first type I had to type an \"@\" symbol I had a \"wow this is super spongy\" reaction to keyboard. It may be a nice USB HUB but it's not a great keyboard.
\n\n\n\nI plugged it in and the Pi 5 immediately powered on (and the fan started spinning, so I'm relieved not to have to spend upwards of two minutes disassembling and reseating the connector). What's a nice contrast to my Pi 400 experience was that I assumed that once I plugged in the monitor, keyboard, and mouse I'd need to reboot because I seem to recall that my old Raspberry Pi didn't send a signal to the monitor if it didn't have a monitor plugged in during boot. But, no, as soon as the monitor was plugged in (still micro-HDMI sockets) everything Just Worked.
\n\n\n\nThe Mac-like menubar at the top of the screen has three icons in the top-left corner, an app menu, a browser button, and a terminal button. Perfect.
\n\n\n\nOh yeah and when I created the image for the machine on my Mac it offered to copy my WiFi credentials onto the image (and triggered a security dialog when I said yes) and it Just Worked. This was a conspicuous pain point for the Quest, and I let it slide because I assumed that Meta must have had some issue with Apple's security stuff that stopped them smoothing it over. But, apparently, Raspberry Pi can do it (and their imager tool looks far more polished than the Meta support apps for the Quest 3 do).
\n\n\n\nI quickly got into my Google and Apple iCloud accounts thanks to the new Passkey stuff which isn't an option for the Quest (and of course Meta hasn't put any effort into helping with this because it's the kind of thing anyone seriously using their product would quickly get frustrated by, and no-one internally seems to be using their product much).
\n\n\n\nSo I was up-and-running much faster than with my Quest 3. Also the thing seems way faster than the Quest 3… it certainly dealt with iCloud Drive and Google Photos very nicely. So now I have a nice desktop picture. Super important.
\n\n\n\nMy next step was to install NodeJS and another nice surprise is that it runs nodejs 20.x (I also note that the Chromium that is preinstalled was v116.x which is pretty recent. I imagine at some point I'll have to do a massive update (apt tells me there's a lot of stuff to update, and I can't be bothered right now). I'm looking forward to seeing if I can build out electron or nwjs apps.
\n\n\n\nI do find the partially transparent Chromium window to be a bit nasty looking.
\n\n\n\nA quick dip into the Chromium inspector shows that MathML and SVG are there. ui.xinjs.net and b8rjs.com both load and run their most challenging demos pretty decently (the babylon3d demo on the b8rjs.com site is a bit sluggish, but reflections and shadows are working). Also timezones.xinjs.net runs very nicely (and that's a pretty gnarly collection of SVGs).
\n\n\n\nb8rjs.com has stress tests which I ran and it seems to be about 25% as fast as my 2021 Macbook Pro M1 Max on the create and render 10k table rows (~1300ms vs. ~350ms), and 20% as fast at the create 100k rows with the virtual data-table test (~1800ms vs. ~360ms).
\n\n\n\nSo I'm about to hit the sack, but overall a much better initial experience that with the Quest 3, despite this being very much not a product for ordinary consumers. Not having to deal with Meta is a huge bonus, of course. Given how well all the Raspberry Pi stuff works, Meta's Quest team should really should hang their heads in shame.
\n","$updatedAt":"2024-06-05T09:10:30.273+00:00",path:"pi-5",_created:"2024-07-09T20:28:31.644Z",id:"8085",_modified:"2024-07-09T20:28:31.644Z","$id":"8085",_path:"post/8085"},{date:"2023-11-18T16:23:36.000+00:00",summary:"What are my initial impressions and thoughts on the Meta Quest 3, its user interface, usability, and related software issues?",keywords:["meta quest 3","vr headset","usability issues","software bugs","pairing keyboard","virtual desktop app","remote display","video capture","mic audio","apple"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:30.268+00:00","$databaseId":"665dfafe000014726b3d",title:"Meta Quest 3—Part Two","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nMost user input on the web (e.g. login pages) is done using forms even though HTML forms are not fit for use and need to be prevented from doing their default thing to work properly since \"Web 2.0\". (Basically, if you hit enter or push a button inside a form its default behavior is to reload the page and lose everything. This is weirdly not what anyone wants but made sense kind of before web pages started communicating with servers directly. This is sad, but one of the reasons the web has been so insanely successful is that it is very forgiving and doesn't break old stuff gladly.) xinjs-ui
provides a simple reusable form wrapper that does all the \"usual things\" people want forms to do and stops bad things from happening while trying to leave everything as much alone as possible. So it lets you use <input type="date">
elements to display and modify date values in a robust and standard way. Guess what flat out doesn't work at all on the Quest's built-in browser?
The built-in interactive demos on the site let me actually quickly test a bare <input type="date">
alongside the \"wrapped\" version that was failing to verify that it's not my code that's the problem. You simply can't enter dates via a date input. So, good luck scheduling calendar appointments or booking airfares on any site that uses standard widgets. (Contrast this with mobile Safari which not only supports such things but goes out of its way to provide native experiences around things like auto-complete.)
I should note that the Quest browser does a great job with <select>
elements. This isn't a failure of engineering, this is a failure of emphasis. Clearly no-one cares if you can get work done using this thing. There's no-one coming into the office in the morning and trying to work using their Quest headset for as long as possible until they reach a blocker and then raging out, writing a bug report, and telling the manager of the team responsible to fix their shit.
Interestingly, the Quest 3 offers beta support for desktop sharing out of the box. I actually paid for a third-party solution for this for my Quest 2, which I was planning to try out on the Quest 3 once I sort out the Quest 3 being attached to the wrong account. Anyway, this looks promising.
\n\n\n\n(Addendum: both the free beta of Remote Display and the commercial Virtual Desktop app are discussed in more detail in the follow-up article.)
\n\n\n\nCapturing Video is pretty easy (meta-right-trigger to start and stop video capture), except that by default it won't capture your mic, and I'd rather narrate my experience than capture silent video and then overdub it. After all, don't you want to know what my user name means in the language I made up?
\n\n\n\nYou can capture mic input by using the \"camera\" app to trigger video capture and manually switching the mic on for that capture, but by default it is always off (I hoped turning it on for one video might change the some underlying setting—it does not—or at least that next time I used that dialog it would default to the previous choice—and no it doesn't do that either. AFAICT there's no way to turn it on mid-way.
\n\n\n\nIronically, streaming your experience is also possible via the camera app and here the default is to include mic audio. Just in case you thought Meta suddenly cared about your privacy.
\n\n\n\nAnyway, I haven't figured out a way to conveniently capture video with mic audio nor have I got stuff syncing to my computer yet.
\n\n\n\nIf you put yourself in the shoes of a usability tester at Meta, consider just how little of a damn they must give about you to make doing all this stuff so messed up. Personally, were I on the team building this stuff, I'd be frustrated just in my own ability to capture quick examples of bugs or other issues and share them and fix it just for my own convenience.
\n\n\n\nThe depth of indifference to usability I read into all of this is mind-blowing. But, never ascribe to malice…
\n\n\n\nAt least one of the emoji used in my previous blog post (and likely this one too) does not render on the Quest 3. Apparently Meta isn't even keeping up with Emoji (and it's not like I'm using super modern obscure ones).
\n\n\n\nAs an Apple shareholder I suppose I should be thrilled that the company in the second-best position to make inroads into the VR / XR / AR space is so clueless, but I really wanted to love the Quest 3. As I said to my girlfriend, when Apple made the iPhone they had Eric Schmidt doing industrial espionage for Google on their board. He went back to Google and told the Android team to stop working on their new Sidekick and instead steal Apple's ideas. Despite this, Apple has maintained a durable technical and usability advantage in the smart phone space for fifteen years. How dominant might they be in the VR / XR / AR space when their competition is this clueless?
\n\n\n\nBack during the mass layoffs in Silicon Valley in 2022 Zuckerberg was supposedly furious that there were a ton of people working on Oculus project that weren't using or only grudgingly using the product. Dogfooding is crucial for any consumer product and your goal needs to be a product you use all the time in preference to alternatives and probably in preference to things that aren't even seen as alternatives.
\n\n\n\nI'm sure the Apple Watch team has people who use their Watch instead of their phone as much as they can. They probably have \"leave your phone at home\" days. I'm sure there iPad team has people who use iPads for things other folks use their Macs and iPhones for. I'm sure there are Vision Pro team members who don't have any monitors, who code on their Vision Pros when they can, who attend meetings with them, and when they run into problems they fix them.
\n\n\n\nAs soon as you internalize the idea that the product you're building is for \"other people\" that you are imagining, you are fucked.
\n\n\n\nThe fact that most Facebook employees avoid Facebook outside of work and won't let their kids use it says a lot about it.
\n\n\n\nAnd yes, I worked for Facebook and no I didn't like it and it didn't like me. And yes, I bought Oculus products post FB-buyout and held my nose despite all of this.
\n\n\n\nMore to come once I pair a keyboard and install Opera and/or Chrome.
\n","$updatedAt":"2024-06-05T09:10:30.268+00:00",path:"meta-quest-3-part-two",_created:"2024-07-09T20:28:32.367Z",id:"7986",_modified:"2024-07-09T20:28:32.367Z","$id":"7986",_path:"post/7986"},{date:"2023-10-25T22:41:47.000+00:00",summary:"What are my concerns and experiences with using Panic Nova for my work, leading me to switch back to VS Code for my day job?",keywords:["nova","reconsidered","panic nova","syntax highlighting","editor instability","crashes","phantom errors","custom syntax support","documentation","web preview"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:30.274+00:00","$databaseId":"665dfafe000014726b3d",title:"Nova… reconsidered","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nI was willing to turn a blind eye to other issues, e.g. the fact that the documentation for its custom syntax support is horrible and implementing simple embedded syntaxes is either impossible or nightmarishly difficult (or, perhaps, perfectly easy but just not documented).
\n\n\n\nThe thing I really like about Nova is having everything in one window on a gigantic screen and the fact Nova remembers my exact window layout. So I can have my file browser, editor, terminal, web preview and debugger, excellent git integration (honestly, it's almost like having Sublime Merge—a git client I like so much I tend to run it all the time just to do things like amend commits and spelunk history—built into the editor).
\n\n\n\nBut if your app needs to do authentication in a popup window, Nova's web preview doesn't handle it, so that's partially out the window, which makes it less attractive for my \"day job\".
\n\n\n\nI really want to love Nova, but the instability and flaky syntax hiliting are a bridge too far. At least for my day job… for now.
\n","$updatedAt":"2024-06-05T09:10:30.274+00:00",path:"nova-reconsidered",_created:"2024-07-09T20:28:32.907Z",id:"7949",_modified:"2024-07-09T20:28:32.907Z","$id":"7949",_path:"post/7949"},{date:"2023-10-14T10:17:26.000+00:00",summary:"What is my perspective on the book \"A Fire Upon the Deep\" by Vernor Vinge, particularly in regards to its ideas, characters, and world-building?",keywords:["a fire upon the deep","vernor vinge","sf novel","computer science","ideas and invention","galactic zones","slow zone","beyond","transcendent gods","internet influence"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:20:52.491+00:00","$databaseId":"665dfafe000014726b3d",title:"A Fire Upon the Deep, Revisited","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\n(This is an AI-generated image linked in a reddit thread talking about generating this image using AI, so I don't feel too guilty using it.)
\n\n\n\nI recently discovered I had collected a lot of credits in the audiobook service I subscribe to and for lack of a better option grabbed a bunch of SF novels I had read and loved twenty or more years ago to see what I thought of them now. (In some cases, they were books by authors I loved which I may not have read). The second of these books I've listened to is A Fire Upon the Deep, by Vernor Vinge.
\n\n\n\nVernor Vinge's day job was teaching Computer Science at San Diego State, and it definitely shows in A Fire Upon the Deep. If you haven't read it, and you like SF, it's wonderful stuff. It doesn't quite qualify as literature in my opinion—Vinge's ability to render character is often weak, a common failing of SF writers—but where it comes to ideas and invention this book is right up there. If you love Iain Banks's Culture universe, as I certainly do, this book seems to have pulled many assumptions from the same zeitgeist.
\n\n\n\nI won't dwell on the book's shortcomings. There's a romantic subplot in the book that is utterly unconvincing and the major non-abstract villain (there's an overarching villain in the story that isn't really a character) is cartoonishly and ridiculously evil and has zero character development. The ending, as endings often are, combines forced tension with forced resolution. It has an ending, which is about as much as can be said for it.
\n\n\n\nBut oh, the ideas...
\n\n\n\nThe core idea of the background is the idea of galactic zones, apparently correlated with mass density, that determine how well complex systems (like intelligence and computers) are able to operate. Being relatively close to the galactic core puts you in the \"slow\" zone where intelligence is lacking and complex computer circuits and faster-than-light communication simply don't work. The furthest reaches, of \"beyond\" are \"transcendent\" and there dwell the gods.
\n\n\n\nThis is not just a superficially examined idea. First of all it's not a fixed thing, and the boundaries experience \"weather\". Next, there is travel and communication between zones. And the zones have their benefits and hazards. Humanity emerged from the slow zone some distant time past, and this may not have been its first emergence.
\n\n\n\nWritten in 1992, like the early Culture novels, Deep is strongly influenced by the pre-Web Internet. Assume a galactic Net that is basically like the internet, but with wildly varying restrictions in speed and bandwidth of communication, Vinge-as-computer-scientist is right-at home exploring the wheels, cars, parking meters, traffic jams, and hub cab thieves of such a network. Some of his insights about trolling and misinformation are prescient. Others, like his assumption that a clearly visible truth will be self-evident and obliterate most malicious speculation now seem hopelessly naive.
\n\n\n\nI particularly like the network postings being affixed with a machine-translation route-path. As in, the series of translations needed to get from the original text to what you're reading. Except of course the language you're reading isn't that language at all.
\n\n\n\nAnd there's a race of plant-cyborgs that don't really operate in human time-frames and have terrible problems with short-term memory (which is one reason they are cyborgs, the other being locomotion). And, yes, they're fun.
\n\n\n\nMuch of the action takes place on a planet in the Deep—the slow zone—where the dominant alien species on a planet is a dog-like species which operates in small packs that have a group—hive mind—consciousness built upon rapid \"networked\" audio communication. And again, this idea is deeply considered with all the parking meters and hubcaps.
\n\n\n\nThe politics of the book are quite pathetically and parochially American. The book's female characters are weaker even than the male. The most interesting possibilities of the alien politics among the hive-mind dogs are basically ignored. It's by no means a perfect book. But the ideas are worth the visit, even if in the end it is not a tour de force like the Ancillary trilogy.
\n","$updatedAt":"2024-06-05T09:20:52.491+00:00",path:"a-fire-upon-the-deep-revisited",_created:"2024-07-09T20:28:33.542Z",id:"7943",_modified:"2024-07-09T20:28:33.542Z","$id":"7943",_path:"post/7943"},{date:"2023-09-25T13:31:26.000+00:00",summary:"What is my perspective on the effectiveness of assault weapons bans in reducing gun violence in the United States?",keywords:["assault weapons bans","gun control","second amendment","handgun regulation","semi-automatic rifles","gun violence","australia","united states","dunblane massacre","supreme court"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:30.268+00:00","$databaseId":"665dfafe000014726b3d",title:"Sensible Gun Laws","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nIt's been a while since I've posted anything overtly political, but I just listened to the third and fourth episodes of the latest series of Malcolm Gladwell's podcast, Revisionist History. Even though this is probably my favorite podcast of all time, I wasn't hugely taken with the first two episodes. The thesis that America's obsession with gun culture is [largely] a result of too many Westerns being on TV in the 50s and 60s has some credibility, but we can't fix it now except by waiting for the deluded to die.
\n\n\n\nBut the third and fourth episodes deliver two huge punches:
\n\n\n\nShooting Lesson discusses the disingenuity of the arguments of gun control advocates, namely that \"assault weapons\" aren't the problem, semi-automatic weapons, and handguns in particular, are the problem, and advocating for \"assault weapons\" bans is a political gimmick not an actual useful argument.
\n\n\n\nMoral Hazard discusses the improvements in trauma care for gunshot victims that, it turns out, is responsible for the decline in homicide rates in the US. Violent crime is up, shootings are up, but owing to improvements in medical care, 95% of gunshot victims are now saved. If we really want to understand what's happening with violent crime, we need to track shootings, not homicides, and there is no methodical capture of this data in the US.
\n\n\n\nAs someone who doesn't (or very rarely) shoots, but knows a fair bit about guns owing to (a) having been a heterosexual boy who grew up in the 70s, (b) being a huge fan of procedural crime fiction, (c) a wargamer, and (d) a role-playing game designer, I was fully aware that assault weapons are neither the problem nor a coherent category (a semiautomatic rifle that isn't an AR-15, lacks a pistol grip, and has a wooden stock is in no way less lethal than an AR-15, the AR-15 is popular because it's modular, so folks can customize and \"hotrod\" them).
\n\n\n\nWhat I wasn't aware of was that gun control advocates are disingenuous about pursuing \"assault weapons bans\" because that they can get people to vote for them. It's a \"something\" they can \"try to do\" to gain votes which will be voted down by their political opponents. Assault weapons bans are to gun control arguments what late term elective abortions are to \"pro-life\" arguments.
\n\n\n\nSimilarly, I knew that trauma care for gunshot victims has vastly improved. E.g. US casualties in Vietnam numbered approximately 60,000 dead and 150,000 wounded while the numbers for Iraq are 4,431 dead, 31,994 wounded, and Afghanistan are 1,921 dead, 20,713 wounded. The ratio of wounded to dead went from 2.5:1 in Vietnam to 10:1 in Iraq and Afghanistan. In other words, 75% of the people killed in combat in Vietnam survive with modern medivac and trauma care.
\n\n\n\nIn contrast to this, the US homicide rate peaked in 1980 at 10.2 per 100,000 people (per annum) whereas as of 2019 the homicide rate was half that, at 5 per 100,000. It seems pretty reasonable to suggest that this \"halving\" of the murder rate took place an increase in the attempted murder rate masked by huge improvements in trauma care. And if you look at the rate for rape, 36.8 in 1980 vs 42.6 in 2019, this tracks. On the other hand, the \"violent\" crime rate has somewhat decreased since 1980, but (a) this includes everything from fist fights to mass shootings, and (b) we also know that statistics-driven policing (\"comstat\" etc.) led to methodical underreporting of \"minor\" crimes since the 1990s. Rape is, of course, a complicated crime to compare to homicide. E.g. society has changed its definition of rape and the threshold for reporting has changed. The big problem is that the US just doesn't track violent crime or gun crime rigorously enough.
\n\n\n\nAnother key point raised in the Moral Hazard episode is that rifles are, on average, far less deadly than pistols simply because it's hard to get in close with a rifle and shoot someone lots of times. Instead, unless the first shot is an outright kill, the person hit falls to the ground and is harder to hit again. Again, the problem isn't \"assault rifles\" but semi-automatic firearms of all kinds, and handguns (the difference in rate of fire between a semi-automatic pistol and a revolver is basically irrelevant).
\n\n\n\nThe crime data I'm using is here if you want to check it.
\n\n\n\nAfter the Port Arthur massacre, the conservative (\"Liberal\" coalition) Australian government at the time took the controversial stance of simply banning semi-automatic rifles. I attended a large rally in Sydney at which the anti-gun crowd was addressed by politicians of all the major parties. Since then, as Jim Jefferies has pointed out in his excellent bit on Gun Control, there have been no more mass shootings in Australia. That said, Australia's homicide rate in 1990 (before Port Arthur) was 2.2 in large part because handguns were strictly regulated. Even so, it was 0.74 as of 2021. I found these stats here. From what I know, Australia is not actually a less violent country than the US (I don't have the stats handy, but last I checked Australia has slightly higher rates for violent crime than the US, they just don't end in shooting deaths).
\n\n\n\nGiven that Australians probably watched all the same westerns as Americans, are just as prone to violence as Americans, and have the same frontier macho dude delusions as Americans, my basic conclusion is that strict handgun regulation eliminates roughly 75% of gun homicides, and banning semi-automatic rifles eliminates another 50% of gun homicides. But banning \"assault weapons\" accomplishes none of this. There are plenty of semi-automatic rifles that aren't \"assault weapons\" and in any event most of the gun homicides are from handguns.
\n\n\n\nAs such, campaigning for an \"assault weapons ban\" is worse than useless. It probably fails, it won't persuade anyone of anything (and in fact hardens opposition from reasonable people who know anything about guns), and it won't stop anyone from being murdered. And if it succeeds it will take the steam out of genuine reforms by (a) not working and (b) allowing folks who voted for it to rest on their laurels. It's virtue signaling at its worst.
\n\n\n\nThe Sandy Hook perpetrator could have accomplished just as much mayhem with a non-assault-style semi-automatic rifle or handguns. In fact the Dunblane massacre in Scotland was conducted with semi-automatic pistols and revolvers.
\n\n\n\nSo it comes down to what the purpose of proposing assault weapons bans actually is. If it's just posturing for the purposes of pretending to have a plan to \"do something\" about gun violence that serves as a bullet point for political platforms then, I guess, mission accomplished. But as a measure to actually address gun violence it serves no purpose.
\n\n\n\nI've been wrong about plenty of things in the past. Back in 2004 or so I was basically against making gay rights or gun control major planks in the Democratic Party platform because I thought they were vote losers and the problems were intractable. I was comprehensively wrong about gay rights but at least the gay rights policies actually addressed real problems and proposed real solutions.
\n\n\n\nIt's possible that \"assault weapons bans\" are a brilliantly nuanced policy that signals to people who would happily dispense with the 2nd Amendment (\"of course you can change it, that's why it's called an amendment\", to quote Jim Jefferies) that they're on our side while not aggravating gun owners too much because they know (a) it will never pass and (b) even if it actually passes they'll just sell their \"assault weapons\" to the taxpayer and use the money to buy the new modular non-assault-style semi-automatic rifles that will promptly become wildly popular and easily available, or just buy handguns if they really want to murder people.
\n\n\n\nI think focusing on \"assault weapons\" is purely counter-productive, and the screen capture suggests Fox News does too. It doesn't persuade anyone and it antagonizes both the unreasonable pro-gun folks who might otherwise not bother voting and the reasonable pro-gun folks who correctly recognize it as disingenuous and pointless.
\n\n\n\nBut, what Assault Weapons Bans aren't is a way to reduce gun homicides or address gun violence. Preventing impulse purchases, age restrictions, background checks, and red flag laws will have some impact at the edges, but a lot of these measures will likely be declared unconstitutional by the conservative idiocracy dominating the Supreme Court.
\n\n\n\nTo actually reduce gun violence in the US, we need to ban semi-automatic weapons, strictly regulate handguns, and come to grips with the insane number of guns already in circulation. And that entails repealing or further amending the 2nd Amendment and probably changing the composition of the Supreme Court or drastically reducing its power by some constitutional sleight of hand (which is also how it got the power it has).
\n\n\n\nWe also probably need to fix a lot of psycho-social problems but that's clearly a second order issue (recall Australians also watched American TV, fancy themselves to be rugged frontier individualists, and so forth, yet have far fewer gun homicides.)
\n\n\n\nFile it under the US system of government is broken and probably can't be fixed.
\n","$updatedAt":"2024-06-05T09:10:30.268+00:00",path:"sensible-gun-laws",_created:"2024-07-09T20:28:34.189Z",id:"7920",_modified:"2024-07-09T20:28:34.189Z","$id":"7920",_path:"post/7920"},{"$permissions":[],date:"2023-09-22T18:11:27.000+00:00",summary:"What is my perspective on the game Starfield, its various aspects and features, and how it compares to other games?",keywords:["starfield","opportunity lost","game review","space opera","fallout","crafting system","loot system","inventory management","ground combat","modding community"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:29.711+00:00","$databaseId":"665dfafe000014726b3d",title:"Starfield: Opportunity Lost","$collectionId":"665dfb040030d12ada24",content:"Here's the spoiler-free summary: Starfield, as a launch-day product, is the best sandbox \"space opera\" game since Mass Effect Andromeda. With some improvements and, presumably, a lot of help from the modding community, it may improve somewhat (and Mass Effect Andromeda has not had the benefit of continued attention from Bioware, unfortunately).\n\n\nIt's basically Fallout without VATS with a 1970s retro-future feel instead of a 1950s retro-future feel, except this seems more like an accident than a decision. E.g. even in the 1970s I'm pretty sure people had telephones and walkie talkies. Computer networks hadn't become commonplace but the internet already existed. So it has a Syd Mead / NASA Publicity Department feel without this being integrated into the setting.\n\n\nOh and the crafting system is like Fallout, but worse (and makes no sense in context), the loot system is like Diablo, but worse, and there aren't Fallout-style perks, and the skill system is padded with skills that make no sense and tiers that simply make the game less fun (e.g. leadership is a top tier skill so being able to have a fourth crew member on your ship—anyone can have three—requires you to devote a crazy number of skill points to things you probably don't care about).\n\n\nI would rate it a C+ and suggest you only buy it at full price if you're super anxious to try it out. Otherwise, play something that's actually good and wait for it to go on sale.\n\n\nBetween us, my sister and I have played through all the major arcs and many of the side-quests and have 36/50 of the achievements (neither of us care a fig for achievements so that's quite a lot). We've \"finished\" the game and even played a bit of what can happen afterwards (it doesn't get better although it does have one nice surprise that I can't really say anything about without it being a huge spoiler). We've also both played Bethesda games going back to Arena.\n\n\n## Inventory\n\nInventory management is a constant annoyance in this game. In a future with no Amazon deliveries where you need to physically walk to a store to buy glue (cough \"adhesives\") there's no concept of even \"let me buy this stuff and stick it in my cargo hold\". I won't even go into all the fractal ways in which it sucks because there are so many. It even perversely affects space combat because if you have a ship with decent cargo capacity (a must if you want to stay sane) then defeating a smaller ship in combat and boarding it leads you to \"OMG do I want to deal with the inventory fallout of switching control to this ship?\"\n\n\n## Ground Combat\n\nIf you're going to take an existing game engine and turn it into Starfield would you pick Skyrim's / Fallout's engine or, say, an engine that can cope with vehicles, stealth, and cover? Like, say, Grand Theft Auto or Watchdogs?\n\n\nBear in mind, probably the best / least bad part of Starfield is anything to do with spacecraft and space combat. Unlike ground combat, space combat has been engineered from scratch and basically works and has a pretty good UI. And you can see more or less where things are. Unlike the crafting system, you can modify your starships without burning dozens of skill points, and you don't need to keep a gigantic inventory of crap to build recipes out of huge and arbitrary component lists to then build parts out of random crap you need to collect using equipment you may not have handy.\n\n\nSo, first of all, there's no cover mechanic. And everything is crazy heights so you'll often find yourself crouched behind a thing that is two inches too tall to shoot from behind while crouched, so you need to stand and immediately be visible to anyone anywhere who wants to shoot you, which they will do with zero delay for aiming.\n\n\nNext, the art direction is such that it's super hard to see enemies. This may be \"realistic\" but it sucks and the enemies can in fact see you through cover, dense foliage, fog, and smoke, so realism is not an excuse. They will find the most obscure line of sight to you and shoot instantly as soon as you, say, one-shot someone with a suppressed sniper rifle while concealed. Which is also one of my gripes about Fallout 3 and Fallout 4. And of course, in order to one-shot someone with a sniper rifle you need to have dedicated a ton of skill points not just to marksmanship but also crafting. Because this is a future where you cannot actually buy stuff you want conveniently / at all. And this is Bethesda where a common mob can typically survive four headshots.\n\n\n## Crafting & Outposts\n\nThey are horrible…\n\nSeems like I didn't finish this review. Oh well, I may come back to it now that Starfield has been improved somewhat and the expansion is out.\n\nTo be continued…?\n\n","$updatedAt":"2024-06-05T09:10:29.711+00:00",path:"starfield-opportunity-lost",_created:"2024-07-09T20:28:36.343Z",id:"7917",_modified:"2024-10-21T21:53:41.927Z","$id":"7917",_path:"post/7917"},{date:"2023-08-29T17:37:27.000+00:00",summary:"What is my perspective on using Nova as a potential alternative to Visual Studio Code for my coding needs?",keywords:["nova editor","text editing","visual studio code","syntax coloring","intellisense","keyboard shortcut","search tools","build files","documentation","extensions"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:29.708+00:00","$databaseId":"665dfafe000014726b3d",title:"nova vs code","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nSimilarly, I feared there was an issue with build files not being ignored in searches, but while, once again, the system for adding ignore paths is not well-explained in the preferences pane and does not provide helpful examples (nor it is possible to edit entries—you need to delete them and add new ones). It took me several attempts to figure out that I need to ignore dist and then getting jsDoc boilerplate when I just want to use markdown, so I'm now using and just want to tell the editor that anything inside this is markdown, and, ideally, anything inside a
<pre>js...</pre>
code block is javascript, and so forth.
Again, Nova seems to support this stuff, but the documentation is abysmal. It tells me that the thing is possible but neither gives an example nor explains exactly how it might work. I get the idea that I probably need to find documentation on creating an extension, but I can't find that. (Edit: I found the documentation!) I tried looking inside the Nova package to see if there are any syntax declaration files in it but no dice. I have literally no clue what the file should look like, nor what to do with it were I to create it.
\n\n\n\nNow, I haven't spent weeks spelunking the documentation (as I have for Visual Studio Code) nor devoted serious time to understanding its extension architecture (as I have for Visual Studio Code). This isn't fair. And I do see evidence that once I do penetrate the veil of documentation, Nova is actually going to turn out to have superb tooling for all this because of references I see to leveraging modern tools for defining parsers and syntaxes which look like things I briefly worked on after getting frustrated by tools like jison.
\n\n\n\nAll of my issues with Nova currently come down to documentation and examples. If I can figure out how to write a simple extension that will help me with writing documentation and a minimal amount of boilerplate, there's really no downside to Nova and the reduction in Window clutter is simply amazing.
\n\n\n\nSo, I'm continuing my experiment and will either discover that the extension situation is awful or too proprietary (I need to be able to support Visual Studio Code users even if I don't use Visual Studio Code myself) or it will allow me to reuse most of the stuff I've done for Visual Studio Code and I'll pay Panic their extremely reasonable subscription fee.
\n\n\n\nIf I hadn't spent six months working in the VS Code extension API when I was at Google and didn't care about supporting VS Code users and being able to reuse or lightly adapt snippet and syntax definitions from VS Code, my guess is I'd be sold on Nova at this point.
\n","$updatedAt":"2024-06-05T09:10:29.708+00:00",path:"nova-vs-code",_created:"2024-07-09T20:28:36.931Z",id:"7867",_modified:"2024-07-09T20:28:36.931Z","$id":"7867",_path:"post/7867"},{date:"2023-08-29T15:05:02.000+00:00",summary:"What is the purpose behind the redesign of thexinjs-ui
table component and how did it lead to a simpler implementation for users while improving its extensibility?",keywords:["xinjs-ui","0.4.0","graphical table filter","filter-builder","table component","virtual table","framework","implement table","sorting","search"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:20:54.360+00:00","$databaseId":"665dfafe000014726b3d",title:"xinjs-ui 0.4.0's graphical table filter","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\nThe main reason xinjs-ui
exists is that I wanted to have a really good table component I could just plug into projects. It would be easier to just use something off the shelf, but virtual table components aren't actually widely available, and those that are are not free, quite heavy, and tend to come with a lot of stack assumptions and dependencies.
Also, being able to implement a really good table is an acid-test for a framework.
\n\n\n\nAs soon as you build a good table, you're going to need to filter it (i.e. search) and sort it. So xinjs-ui
has a really slick <filter-builder>
and a simple but powerful makeSorter
function for creating complex callbacks for Array.sort
.
The original <filter-builder>
was driven by a syntax inspired from google search (in essence instead of tokens like site:foo.com it allowed haystack:needle, but provided alternatives to the ':' allowing comparisons such as '<' (less than), '>>' (after), '=' (equals, vs contains) and so on.
While this seemed very powerful and clever and efficient to myself and my colleagues, the users struggled with it, and so I provided extensive online-help, automatic hints, and added features such as support for quoted strings with spaces, and so on.
\n\n\n\nYesterday, I tore all this up and replaced it with a graphical user interface inspired by Apple's Finder (but simpler and more explicit than that). While it took me a couple of months to figure out exactly how to implement this, the code is now actually simpler and has fewer failure modes. Instead of parsing a simple grammar, the user interface enforces sensible inputs.
\n\n\n\nThe old contains
FilterMaker
looked like this:
{\n hint: 'field:value',\n explanation: 'field contains value, ignoring case',\n description: (field: string, value: string) =>\n `${field} contains "${value}"`,\n token: /^([^\\\\s]+?):(.+)$/,\n makeFilter: (field: string, value: string) => {\n value = value.toLocaleLowerCase()\n return (obj: any) =>\n String(obj[field]).toLocaleLowerCase().includes(value)\n },\n}
\n\n\n\nThe new version looks like this:
\n\n\n\ncontains: {\n caption: 'contains',\n negative: 'does not contain',\n makeTest: (value: string) => {\n value = value.toLocaleLowerCase()\n return (obj: any) => String(obj).toLocaleLowerCase().includes(value)\n },\n}
\n\n\n\nThe new <filter-builder>
is simpler to extend because writing a new FilterMaker
doesn't involve writing a function to describe the resulting test function, regular expressions to parse the token, and most filters can easily be configured to provide both positive (\"contains\") and negative (\"does not contain\") variations. The makeFilter
function is simpler because it doesn't look at objects, just values. The order in which FilterMakers
are evaluated no longer matters, which means I don't need to explain that in the documentation.
As usual, it seems like a really good design decision is a virtuous circle: easier to use, less complex to implement, and easier to extend. As of writing, filter-builder.ts
is 434 lines including 100 lines of inline documentation with live example. The old version was 384 lines without documentation.
Post Script
\n\n\n\nI just realized the simplification win was further understated because the amount of CSS required to make this all work actually got smaller, and the new component is self-contained with respect to CSS while the old one was not!
\n","$updatedAt":"2024-06-05T09:20:54.360+00:00",path:"xinjs-ui-0-4-0-s-graphical-table-filter",_created:"2024-07-09T20:28:37.463Z",id:"7859",_modified:"2024-07-09T20:28:37.463Z","$id":"7859",_path:"post/7859"},{date:"2023-05-06T11:41:55.000+00:00",summary:"What is my perspective on the quality of \"Strange New Worlds\" and its representation of Star Trek?",keywords:["star trek: strange new worlds","tv show review","episode analysis","plot holes","unrealistic scenarios","science fiction","space adventure","gorn","brown dwarf","black hole","spock","mind meld","science fiction","star trek","cbs"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:24:50.979+00:00","$databaseId":"665dfafe000014726b3d",title:"Strange New Logic","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\n\n\n\n\nHow anyone can take a show whose main character has Anson Mount's coiffure in Strange New Words seriously at any level escapes me. I mean, if the show featured scenes of his being interrupted during his daily two hour ritual, or if his cabin included some mysterious high tech device that turns out to be a hair styling machine, or perhaps he uses the transporter to vaporize himself every morning and produce a properly styled copy—ok, maybe. But perhaps that's the point. Maybe they're just telling us \"hey this is a fun, silly show about good looking people styled to look like comic book characters on space ships having pirate adventures\".
\n\n\n\nThe thing is, good stories need to take themselves just a little bit seriously to be good stories. Even off-the-wall screwball comedies benefit from having a vaguely coherent through line. The Monty Python folks cared about story when they were making Holy Grail and Life of Brian. We all know when a story is just carelessly made up as the teller goes along. It's what four year olds do.
\n\n\n\n\n\n\n\n\nNo, really, we'll explain the malevolent black cloud and the bear and the gate to Siberia and the gravity and the lottery numbers and it will be science! No, fuck off, it's not purgatory. There's no magic! Who took my cocaine? Get me David Lynch on the phone—he'll tell us how to make it all make sense in the end… What's that? Owls? Creamed corn?
\n
My first reaction to Strange New Worlds was that this might actually be a solid attempt to at least just do a better \"The Original Series\" (except, you know, without updating the basic tenets to identify and wash away today's pressing issues, which would make it a true remake and worthy).
\n\n\n\nThis is because, deep down, I want Star Trek to be good and am a crazy optimist.
\n\n\n\nAs is usual with every Star Trek series since The Next Generation, Strange New Worlds is a beautifully put together show with a great cast, incredible production values. We can't train people to write or edit halfway decent scripts, but we can churn out great camera operators, actors, and production designers.
\n\n\n\nPerhaps the only obvious (non-story) blemish is a godawful theme tune that sounds like one of those low-budget \"make it sound as much like this famous theme as you can without getting sued or hiring a competent orchestra\" except they could hire a competent orchestra. Just pay the estate of the original composer and update the arrangement or do something new. Or pay James Horner's estate for the Wrath of Khan music. Good grief.
\n\n\n\nAlso, as usual with most Star Trek stuff, they had to put Spock in it. Now, given it's set on the pre-Kirk Enterprise (in, um, The Original Timeline? It's not consistent with The Original Timeline but neither is The Original Timeline. Trying to reason about Star Trek is like trying to make origami cranes out of pasta) they have plenty of lazy fan-service. It seems to me that most Star Trek at this point could be called Star Trek: Spock, or sometimes Star Trek: Spock will Show Up, and that the primary goal of every story will be to demonstrate that Uhura was crazy over-qualified. Strange New Worlds actually hints that the job of communications officer might involve, I dunno, facilitating communications and stuff. If she could stop boasting about how many languages she speaks like a blacker, sexier, slightly-less-effeminite C3PO that would be even better.
\n\n\n\nAs an aside: I'd add that anyone who speaks as many languages as Uhura purports to would (a) not have a ready answer for the question \"how many languages do you speak\" and (b) would tend to underestimate. But hey, that's like the one-hundredth stupidest thing. I don't even have time to go into the deluge of gobbledygook that continues to be how Star Trek establishes \"so and so is smart and knows stuff\".
\n\n\n\nPerhaps the most novel idea in Strange New Worlds—setting aside the premise that people seeing Anson Mount in person or on comms could keep a straight face—is the ship's doctor keeping his terminally-ill daughter in, I dunno, transporter suspension, while he searches for a cure for what ails her. (Clearly, he has completely convinced himself that transporters do not vaporize you and then assemble a copy despite all the evidence to the contrary.) If you just accept the premise of this storyline (which defies all logic) then it works at an emotional level—you know, like The Walking Dead—but, like Ross's monkey, conveniently disappears when they encounter a Godlike Omniscient Being™.
\n\n\n\nBut we're getting ahead of ourselves. Because there's episode 4, Memento Mori, to remind us that special effects are cheap compared to continuity, respect for the audience, and proof-reading.
\n\n\n\nEven the V'ger episode of The Original Series cannot compare with Memento Mori for gibberish dialog that is so bad it seems like the writers are actively mocking the fans. \"We're going to shove this bullshit down your throats and you're going to like it, because you are contemptible simpletons who thought Picard is a work of genius.\"
\n\n\n\nAside: this is from memory as I sure as hell don't want to watch it again and can't find the script online, but in the V'ger episode the Enterprise fires a photon torpedo at V'ger. It has no apparent effect. Kirk says something like \"nothing can withstand that kind of force\". V'ger fires back. Spock says it had the force of ninety photon torpedoes, and Scotty says that they can only survive another few hits like that. Anyway, Memento Mori makes V'ger look like The Martian.
\n\n\n\nMy lambasting of The Walking Dead at least required you to consider what we know about The Real World (e.g. how a tank works, how to look out for things that want to murder you, how many guns there are in the US, how much ammo police carry around) to understand how stupid the story was…
\n\n\n\nMemento Mori is much more elegantly self-contained. You don't need to remember anything for more than a few seconds or remember anything you learned in middle school to be reduced to incredulity. It literally has a character lament the fact that they cannot raise the shields followed immediately by another character lamenting the fact that the shields are losing strength.
\n\n\n\nIt literally has the captain—who has just evacuated a landing team out of caution—raise shields because a ship won't identify itself and then upon hearing that they've opened comms—immediately lower them. What if they're just going to say \"Eat this human scum?!\" It's just crazy stupid.
\n\n\n\nThe ship turns out to be full of survivors of a mysterious alien surprise attack and needs immediate evacuation, but it's so heavily shielded as to prevent the use of transporters (the people, you see, are put inside the area with the radioactive cargo for safety, I guess?). Weird because the Enterprise is made of stuff that lets it submerge in a Brown Dwarf and skirt a Black Hole's Event Horizon (you know, later in the same episode) and its transporters seem to work fine. Well, forget that. No-one who watches Star Trek cares about pesky details. So we'll use this standard tube we keep for just such occasions that we've never seen before. Great thinking captain!
\n\n\n\nNow despite the fact that we've established the captain is cautious (this is how you show he's not just Kirk all over again, after all)—kind of—and the security officer is completely paranoid, no-one mentions that this will stop the shields from being raised despite the fact that they're rescuing survivors of an unprovoked surprise attack and that the security officer is acutely aware of the issue (you know, she raises it a few minutes later in the same episode). Nor does anyone suggest an obvious alternative that has been used many times before and doesn't preclude the use of shields: the shuttle.
\n\n\n\nNope. Our captain is decisive and brilliant and his officers—who are by no means unwilling to express differences of opinion—recognize this.
\n\n\n\nInstead, they use the tube and then they're immediately attacked. Because he's both brilliant and decisive, the captain instantly gives the order to raise shields (shouldn't he just have a button for that?) but the security officer points out that this won't work because of the tube. Gosh darn it. How could we have known? Anyone, no matter how brilliant and cautious and decisive and well-informed could have made such a mistake, no matter how brilliant and well-trained and outspoken their team of outstanding subordinates were.
\n\n\n\nBecause this is Star Trek we have no way to know whether they could get everyone out of the tube in 15s and then drop it and raise shields. We don't have any discussion of options or alternatives before or after. We have no background to draw upon because Star Trek is nothing if not inconsistent. All we know is that in this instance, the Enterprise survives a huge amount of pounding in a period of time sufficient to complete the colony ship's evacuation but takes very few casualties (and, oddly, as it turns out, more among the crew than among the evacuees, exposed in the tube) while giving the very strong impression that doing this stood a very good chance of getting everyone killed. Because: tension.
\n\n\n\nWhat we do get is super urgent desperation to finish the evacuation, so they can raise shields and maneuver, combined with breathless blow-by-blow reports of how the shields are getting hammered. Again, we can't expect Star Trek to be consistent with previous episodes, or canon, or… waves hands helplessly in the direction of Common Sense. But this reminds us that we can't even expect two successive lines of dialog to jibe.
\n\n\n\nIt stands to reason that they could have told people to get out of the tube, dropped it, and raised their shields far more quickly. And again, no discussion. It kind of worked, all is forgiven. We definitely won't discuss or revisit any of these decisions.
\n\n\n\nNow it's time to do a submarine bit! So they flee into a Brown Dwarf where the captain is willing to gamble everyone's lives on his ship being able to take more pressure than (a) the designers of the ship say it can and (b) mystery alien ship they don't know anything about. Because life is the most valuable thing… don't bother me with your so-called logic! It's this kind of human emotional reasoning that proves Vulcans might be smart but they're always wrong.
\n\n\n\nWe can't expect a Star Trek fan to care about, say, simple Newtonian physics. So, deep in the Brown Dwarf where their sensors by good fortune are slightly better than the enemy's, they have the Enterprise drop a torpedo on a the enemy ship with the reasoning that because the torpedo is so dense it will just fall out of the tube. You know, the way things in free fall don't. Oh and this is because the torpedo's sensors won't work in the Brown Dwarf because reasons.
\n\n\n\nSo because of made up reason we will brilliantly do stupid thing that makes no sense and would never work and it works! Star Trek at its best. Once again, we have shown our hero, Mr Hairstyle, is a decisive and brilliant out-of-the-box thinker.
\n\n\n\nSadly, this reveals the Enterprise's position to the mother-ship. Now they know it's exactly somewhere near that giant explosion plus however far it's moved since then. Got 'em! See, I knew we weren't close to the 43 minute mark!
\n\n\n\nWell, the brown dwarf is being sucked into a black hole. OH. MY. GOD. MORE. PERIL! But it's hard to see what's going on. Spock will go check it out. If nothing else, it will be handy practice in case they someday encounter a Giant Space Amoeba! But wait, he'll take the Security Officer because he can do mind melds and we haven't done that yet and this episode needs a flashback to build back story. Turns out, Gorn bad. Security officer traumatized. Wow. Deep. PTSD is a another thing we can't cure. Got it, mission accomplished.
\n\n\n\nLook, they're in a brown dwarf being sucked into a black hole pursued by mysterious aliens of unknown capability (but, you know, Gorn—the lizard-folks that future Star Fleet has never encountered until Kirk's duel, but everyone in today's Star Fleet knows about), and it turns out Spock isn't going to be left to die because mind-meld, instead we're going to do the whole \"pretend our sub got sunk\" maneuver but make it all science-fictiony.
\n\n\n\nSo, let's slingshot around a black hole and pull enormous g-forces because—and this is just science—we're in free fall and things in a ship experience gravity in free fall while the ship doesn't. Then, at the exact right point, we fire a torpedo—no wait, we've established that we're all out of torpedoes, so something else—full of crap (a show runner perhaps?) out the back and they'll think it's us blowing up because of red-shift. Yeah, that's the ticket.
\n\n\n\nIt's not like Strange New Worlds is dumber than most other tentpole CBS series. The contempt for the audience I see in shows ranging from Criminal Minds, FBI, NCIS, Picard, et al makes the fact that a series like Evil, The Good Fight (both from the same production company and creators), or even Seal Team, could come out of the same orifice even more of a miracle. I guess Star Trek fans have no-one but themselves to blame. If you throng to abject garbage like Strange New Worlds, that's what they'll give you.
\n","$updatedAt":"2024-06-05T09:24:50.979+00:00",path:"strange-new-logic",_created:"2024-07-09T20:28:38.559Z",id:"7524",_modified:"2024-07-09T20:28:38.559Z","$id":"7524",_path:"post/7524"},{date:"2023-04-05T20:43:16.000+00:00",summary:"What is my perspective on popular JavaScript tools and libraries in 2023?",keywords:["bundlejs","parceljs","fantasticon","ts-standard","javascript tools"],author:"Tonio Loewald","$createdAt":"2024-06-05T09:10:20.066+00:00","$databaseId":"665dfafe000014726b3d",title:"Misc. bits of nice tech I've been playing with","$collectionId":"665dfb040030d12ada24","$permissions":[],content:"\nIn the course of building out xinjs I've been discovering all kinds of interesting new libraries and tools, some of which have been inspiring, some disappointing, and others a bit of both.
\n\n\n\nparceljs
is to webpack
, et al, kind of what standardjs
is to eslint
, an opinionated convenience wrapper that does a bunch of stuff so you don't have to. Unlike standardjs, though, it is highly configurable still.
In essence, parceljs will both provide a dev server with watch and reload from your front-end app pretty and do production builds pretty much automagically. It figures out from your entry-point what it is that needs to be done and does it.
\n\n\n\nI find it works particularly well with [bun](https://bun.sh), and conspicuously better than bun's built in dev-server (bun dev
) which I find unstable, inflexible, and an incomplete solution (bun parcel
does what bun dev
does, but makes fewer assumptions about your stack, while bun parcel build
does a great job of building your site for deployment.
The only reason I haven't given it 5/5 is that some of the config options don't seem to work as advertised (distDir
for example) and it doesn't do things like build webfonts from icon libraries out-of-the-box (but see fantasticon
above!).
I should probably downgrade ts-standard
to 2/5 given that it enthusiastically inserts bugs in my code, but I'm still using it so…
Anyway, if ts-standard
didn't screw up foo.bar || fallback
by turning it into foo.bar != null || fallback
or some such nonsense, I'd happily upgrade it to 4/5.
I seem to have made it over the hump and now prefer coding with xinjs to b8js. The one annoyance is that WebComponents involve manually writing a lot more binding (data and event) code than is the case with b8rjs components because the only thing being bound by xinjs
is the value
property of the component.
One thing I discovered (as a result of learning about [bundlejs](https://bundlejs.com/), which is a great tool for analyzing the size of code bundles (and thus boasting about the small size of one's libraries) is parcel
, a tech-stack-agnostic tool for quickly building out web-tech projects. Unlike many of its constituent parts, which are super-fiddly and highly oriented towards the React stack, parcel just doesn't seem to care.
And, in using parcel with [bun](https://bun.sh/), is just bliss, and gets around the fact that bun dev
might be great for interactive debugging with hot reloading, but it doesn't cut it when it comes time to build for production.
Parcel also vastly reduces the pain associated with shipping small libraries. This let me break useXin
out of xinjs
proper and make it a standalone library ([react-xinjs](https://www.npmjs.com/package/react-xinjs)), meaning that React is no longer needed for vanilla xinjs
projects.
Because xinjs
works so nicely with ReactJS it's nice to be able to consider widget libraries entirely on their merits versus being put off by dependency on React. (I haven't looked into integrating xinjs with Vue
et al, but imagine it will be easy enough. Similarly, xinjs
should integrate well with Angular
module futzing around with \"execution zones\".
What's really frustrating about ReactJS is that it's hardly \"one ecosystem\". Each widget library has its own styling schema. TailwindCSS looks interesting, but it's designed with a specific, fairly crippled,. React library. Meanwhile MUI looks great but has its own CSS architecture.
\n\n\n\n[Shoelace](https://shoelace.style/) is something I've been waiting for—a decent library of [web-components] that mostly just work. Naturally they work just fine with xinjs
although the mechanism for working with them is a little flaky.
tldr; if you use a Mac as more than a glorified Chromebook, buy
As I was tediously navigating folders in an \"Open…\" dialog today, it struck me that I was doing something stupid and unnecessary. Whatever happened to Default Folder, the shareware app I used to install on every Mac as soon as I first logged onto it? Didn't I have a current license FFS?
\n\n\n\nNow, Default Folder X requires a lot of permissions. In today's buttoned-down world, it needs to ask for permission to do lots of things that seem suspect—it is, after all, keeping track of every file and folder you look at. As a result of this, I've not been installing it on computers managed by IT departments and, for the last several years that's been most of my work computers.
\n\n\n\nSo, when I started using my new Macbook Pro in 2021 I forgot to install it. And because I'd gotten so used to dealing with file dialogs without it, I just never got to it.
\n\n\n\nLet me just say this: if you use a Mac as more than a glorified Chromebook, get Default Folder X
. It's the next best thing to Apple fixing file dialogs (which they're never going to do—I literally had an argument with the Human Interface team at WWDC about it back in the 90s).\n\n\n\nWhat is Default Folder X?
\n\n\n\nWaaay back in the 90s there was a collection of UI hacks for the Mac called NOW Utilities which did some amazing things. I won't go into it, but some of those things are still sorely missed. The most useful of these was called Super Boomerang (itself a refinement of Boomerang). Boomerang had two main tricks:
\n\n\n\nSo, for example, if you wanted to load a picture from Photoshop into Quark XPress you could go to Photoshop, export the picture. Then you could jump into Quark XPress and import, find the folder you just saved the picture to and VOILA it was already selected (it was the last thing you saved in that folder) and done.
\n\n\n\nSimilarly, if you were going through a folder full of documents it would keep your place for you.
\n\n\n\nBoomerang is long gone but its functionality (and then some) lives on in Default Folder, which became Default Folder X when Apple transitioned to Mac OS X (now macOS).
\n\n\n\nAside from its major improvement over Super Boomerang (i.e. it still works and you can buy it) Default Folder two truly magnificent features and a bunch of useful but less important stuff.
\n\n\n\nThe user interface of Apple's file dialogs has been a sore point with Apple's Human Interface Group for decades. They've tried to provide people with partial replacements like minifinder, launchpad, spotlight app launching, the sidebar, and so forth. The fact is that while Finder itself (especially the spatial Finder pre Mac OS X) is an absolute triumph of usability, the open save dialogs are borderline incomprehensible to most people.
\n\n\n\nDuring a feedback session at WWDC I got up and told the Apple Human Interface Group that the answer was staring them in the face. Replace file dialogs with the Finder. When you clicked open, just switch to Finder and visually filter what the user could see with the file handling properties of the application you were in. When you clicked save, just switch to finder and put the file in a tray or default destination and let the user drag it where-ever they wanted it.
\n\n\n\nMost of the audience got it immediately, but the Apple Human Interface Group simply couldn't wrap their brains around this idea.
\n\n\n\nThey've had 25 years to convince themselves they thought of it and it clearly isn't going to happen. Who knows, perhaps my reader has contacts in Apple. If so, I have a really great idea for the Contacts app…
\n\n\n\n\n","$updatedAt":"2024-06-05T09:10:29.714+00:00",path:"default-folder-x",_created:"2024-07-09T20:28:41.141Z",id:"7461",_modified:"2024-07-09T20:28:41.141Z","$id":"7461",_path:"post/7461"}],latestPosts:["amadine-is-my-new-favorite-vector-graphics-editor","note-to-self-preventing-images-from-triggering-layout-shifts","why-csv","xinjs-blueprints","currentcolor","15-elements-of-composition"],"post/path=amadine-is-my-new-favorite-vector-graphics-editor":{keywords:[],title:"Amadine is my new favorite vector graphics editor",path:"amadine-is-my-new-favorite-vector-graphics-editor",content:"\n\nIt's not perfect (if you look carefully at the screenshot, you'll see a few \nunnecessary vertices in this boolean) but right now Amadine \nseems to be the best lightweight vector graphics tool in the Mac App Store.\n\nAnd it has a lovely icon!\n\nSince Graphic seems \nto be abandonware, and never quite lost its rough edges,\nAffinity Designer \nfeels bloated, Sketch \nstopped added a subscription model and stopped adding features, I've been \nlooking for a new tool for editing UI elements.\n\nAmadine passed my \"create the xinjs XR logo\" test, which all of these apps\n(and several besides) comprehensively failed the last time I looked at the\nlandscape.\n\n## Post Script\n\nAmadine may not be perfect, but the stray vertices aren't its fault. Turns\nout they're in the subtracted object.\n\nAnyway, I went back to the constituents of the boolean and tried to \nrecreate them more cleanly, and Amadine introduces a similar number of \nspurious vertices to the original \"best in class\". \n\nSo, while this doesn't make Amadine worse than any of its competitors, \nit does make its superiority more marginal. \n\nSo Amadin wins on functionality by a whisker, plus it beats\nAffinity Designer on bloat, Sketch on not having a subscription model, and\nGraphic on still being under development.\n\n",date:"2024-11-18T17:04:56.213Z",summary:"Tired of clunky vector graphics apps? Amadine, a new Mac App Store gem, might just be the lightweight, elegant solution you've been searching for. While not flawless, it surprisingly nailed a design challenge others consistently missed. Is this the vector editor we've been waiting for? Find out more in my full review.\n",author:"Tonio Loewald",_created:"2024-11-18T17:04:32.719Z",_modified:"2024-11-25T11:48:15.156Z",_path:"post/1bmtchcg6uxo"},"post/path=note-to-self-preventing-images-from-triggering-layout-shifts":{keywords:[],title:"Note to self—preventing images from triggering layout shifts",path:"note-to-self-preventing-images-from-triggering-layout-shifts",content:"\n\nIt took me a minute to figure this out, so I think it's worth a quick note to my\nfuture self and who knows, it may save someone a minute or two one day.
\nThe question is, how to set an image's size so that it displays at the \nlargest size that makes sense, but doesn't trigger a refresh when it loads.
\nAssuming you're width constrained, the answer I came up with is:
\nmax-width: 100%
so it doesn't overflow the available spacewidth: (image.naturalWidth)px
so it doesn't except its actual widthaspect-ratio: width / height
so it keeps the right aspect ratioIf you're height-constrained then just set the max-height
and height
\ninstead.
\n\nNote that if you set both
\nwidth
andheight
themax-width
squashes the image,\nand if you useobject-fit
it ends up cropped or with empty \"bars\".
The way this works in my blog editor is that when it generates markdown it also\ngoes through all the <img>
elements in the wysiwyg editor and if they're loaded\nit sets the attributes of the element.
It's simple and it works, and now I'm not getting dinged by Lighthouse for layout\nshifts.
\n",date:"2024-11-15T22:09:25.673Z",summary:"A reminder to myself on how to use `max-width`, `width`, and `aspect-ratio` to get nicely sized images that load without triggering unnecessary layout shifts.\n",author:"Tonio Loewald",_created:"2024-11-15T21:54:23.713Z",_modified:"2024-11-15T22:09:30.326Z",_path:"post/vn370g9b38ao"},"post/path=why-csv":{keywords:[],date:"2024-11-15T21:10:18.071Z",summary:"CSV. It's a mess. A truly awful format for data, used either through ignorance or stupidity. It is to data storage as the Imperial System is to weights and measures. Why do we keep using it? \n",path:"why-csv",author:"Tonio Loewald",_created:"2024-09-05T15:41:46.007Z",title:"Why CSV?",_modified:"2024-11-15T21:10:22.444Z",content:"\n\nCSV is a terrible formst for data. TSV is\nsuperior in every way, and XML is far more robust (but horribly verbose).\nSomehow, CSV remains popular.\nSoftware engineers seem completely resigned to\nit, often supporting CSV and not even allowing\nTSV, let alone subtly suggesting it by, say,\ndefaulting to it in export options.\n\n## What's wrong with CSV?\n\nthe basic problem with CSV is that it uses\nfairly common characters (comma and the\ninch symbol commonly used as a quotation\nmark) to separate data fields that may include\nboth commas and quotation marks.\n\nThis means that, despite its apparent simplicitly,\nCSV requires a stateful parser to ingest\nand in fact cannot be handled perfectly. \n\nThere is in fact no standatd for CSV.\n\nA quick search for the \"best csv parser for javascript\"\nyields lots of articles listing alternatives.fast-csv
\nhas a bundle-size of 57kB (16kB gzipped).\n\nIt's ridiculous.\n\n## What's great about TSV\n\nTSV uses the ASCII control charactets (tab and\nnewline) that were literally designed for the express\npurpose of delimiting tabular data. If you need to\ninclude tab and newline characters within the data,\nthere's a simple standard way to do it.\n\nNot only are there dedicated keys on every keyboard for dealing\nwith TSV, many text editors and word-processors will do a pretty\ngood job of displaying the content of TSVs nicely, and the files\nwill tend to be slightly smaller.\n\nParsing TSVs is so simple that it doesn't really deserve the word \"parsing\". You don't need a library,\nin TypeScript / Javascript it's basically one line of code…\n\nfunction parseTsv(source: string): string {\n return source.split('\\n').map(line => line.split('\\t'))\n}\n
\n\nPlease, **please** stop using CSV.\n",_path:"post/cuyd49oxcl1z"},"post/path=xinjs-blueprints":{keywords:[],title:"xinjs blueprints",path:"xinjs-blueprints",content:"There's this really cool annoying feature of Chrome's Lighthouse\ntool that nags you about unused code in a project. Here's the report I just\ngenerated from ui.xinjs.net—a site that Lighthouse \ngives a score of 100 (desktop) and 89 (mobile) to that is hosted on Github.
Now a lot of this stuff is kind of wrong (the fact that a piece of code hasn't\nbeen used yet doesn't mean it won't be used, and it's inefficient to load\neverything in pieces), but underneath it all, when you're looking at real\nprojects (vs. demo sites intended to exercise pretty much everything in a\npackage), dead code stripping works really badly and there are deep issues\nwith the way packaging code handles dependencies.
\nIf you write a package, foo, that uses some stuff from another package named\nbar then there's a pretty good chance most or all of whichever version of bar\nyou used will end up embedded in the distributable version of foo.
\nThis is fine—possibly even good—for small things. E.g. if you use someone's\nleftpad \nfunction in your library it gets broken in a way that would destroy your \ncode, your code will be fine until you update it, and then presumably your \ntests will discover the issue and you can fix it before you ship a new version.
\nBut when you're writing very popular foundational code, like, say,\nReactComponent
it could mean that a project that uses many differently sourced\nsubclasses, e.g. UI components, built at different times by different people,\nyou may get many copies of the same code (inefficient) or even versions (scary)\nin the same codebase.
Both b8rjs and xinjs \nhave an additional problem with this because they rely on the \nSingleton pattern\nto manage application state. The basic idea is that if you look at app.user.name
\nyou'd prefer it to be the only app.user.name
. And if you've somehow spun\nup multiple registries, you no longer have a single source of truth.\nThis is both inefficient (waste of memory etc.) and bad (bugs!).
The problem manifests in React in a more subtle, but actually far worse, way.\nIn React, if there are breaking changes in React then components built on\none version will not play nicely with one another, either obvious or (worse)\nsubtle ways. This leads to React projects getting \"stuck\" on certain weird \ncollections of dependencies.
\nAs xinjs-ui has evolved, it has grown quite big, and \ndead code stripping just doesn't seem to work properly, so if I want to use \none or two components from xinjs-ui
you end up with all of it in your\npackaged code. The more I've looked into it, the gnarlier it seems to get.
So, I wanted to allow components to be built in as lightweight a fashion as\npossible, and the solution I came up with was to turn the class definition\ninside out. Instead of importing the base Component
class and subclassing\nit, export a function that expects to be passed the base Component
class\nand anything else that seems handy (since none of it costs anything). Then\nyou just import the function, pass it to makeComponent
and voila!
But this idea turned out to be even better. E.g. if you wanted to use\ntwo components with the same name (because how many different ways are there\nto name a popup-menu component?) you could take the function and pick a name\nfor it.
\n\n\nimport { Component, elements } from 'xinjs' // congratulations, you just got a copy of xinjs!\n\n
export class MyComponent extends Component {\n content = () => elements.div('hello, world')\n}
\n
export const myComponent = MyComponent.elementCreator({tagName: 'my-component'})\n
\n\nimport { XinBlueprint } from 'xinjs' // just a type declaration!\n\n
export default myBlueprint: XinBlueprint = (tag, factory) => {\n const { Component, elements } = factory
\nclass MyComponent extends Component {\n content = () => elements.div('hello, world')\n }
\n
return {\n type: MyComponent\n }\n}\n
At the cost of a little boilerplate (sigh) you get a component that has zero\ndependencies and whose tagName is chosen by the consumer. But, it gets better…
\nIf you just want to bundle everything together, you can just do the usual stuff.
\n\n\nimport { makeComponent, elements } from 'xinjs'\nimport myBlueprint from 'my-blueprint'\n\n
const { creator } = makeComponent('foo-bar', myBlueprint)
\n
document.body.append(creator())\n// same as document.body.append(elements.fooBar())\n
But if you want to load a component from a cdn or a service endpoint on \ndemand, you can use the new xin-loader
and xin-blueprint
components:
<xin-loader>\n <xin-blueprint \n tag=\"my-container\" \n src=\"path/to/my-container/blueprint.js\"\n ></xin-blueprint>\n <xin-blueprint \n tag=\"my-widget\" \n src=\"path/to/my-widget/blueprint.js\"\n ></xin-blueprint>\n</xin-loader>\n<my-container>\n <my-widget></my-widget>\n</my-container>\n
\n\nAll the blueprints inside a loader will be loaded asynchronously in parallel,\nand of course you can generate them in code:
\nimport { blueprint, blueprintLoader, myContainer, myWidget } from 'xinjs'\ndocument.append(\n blueprintLoader(\n blueprint({tag: 'my-container}, src: '.../my-container.blueprint.js'})\n blueprint({tag: 'my-widget}, src: '.../my-widget.blueprint.js'})\n ),\n myContainer(myWidget())\n)\n
\n\nAnd to wrap all this in a nice developer experience, there's \ncreate-xinjs-blueprint.
\nThe goal here starts with wanting to break xinjs-ui\ninto a collection of easily integrable lightweight blueprints, which means\nI want to be able to:
\nxinjs-ui
is turned into a collection of blueprints)While playwright is a fantastic tool for end-to-end testing, having tests run\nright in front of you on refresh during development is simply amazing, and this\nwas accomplished by turning the test component I use internally for xinjs
\ndevelopment into a standalone blueprint.
It's also incredibly liberating to be able to just build something cool without\nworrying if it is going to add fat to a component library. (There used to be\na bunch of babylonjs-based 3d components in xinjs-ui
but I removed them \nbecause no-one needed them and they bloated the library.)
The first non-trivial blueprint I built was based on the \nSVG clock demo from b8rjs.\nThing is, this demo lives inside b8rjs, but it isn't exported to prevent bloat.\nBut here's the xin-clock component, \nand here's the live demo.
\n\n\nYou're currently looking at the xinie
project, which is my current side-project.\nAmong other things, it replaced wordpress as my blog engine. I'm dogfooding it.
xinie
is a brand new soup-to-nuts, service compris application framework, \n(initially build on Google Firebase) and it supports code-as-a-service \nout of the box, and blueprints are just code, of course.
Already, you can import a module directly into xinie and then embed it in \na page, but ultimately you'll be able to create and edit them, add tests,\nand so forth. And all of it lives inside the fine-grained access framework\nbaked into xinie
.
\n\nEvery so often I discover that CSS has some feature I've kind of wanted for\nyears and didn't realize was there. Today I learned about/* Keyword values */\ncolor: currentcolor;\n\n
/* <named-color> values */\ncolor: red;\ncolor: orange;\ncolor: tan;\ncolor: rebeccapurple;
\n
/* <hex-color> values */\ncolor: #090;\ncolor: #009900;\n...\n
currentcolor
.\nThis feature is mentioned here \nand it links you to here,\nbut you'll need to go find blog posts that explain just how darn useful it is.\n\nWhat is it? It's basically a special variable that contains the currently set\ncolor. It's pretty much exactly what you want for, say, monochromatic svg icons\nalmost all of the time:\n\nsvg {\n fill: currentcolor;\n}\n
\n\nNow, wouldn't it be nice if there were a similar value for background-color
?\nYou can set background-color
to currentcolor
(which is not helpful) but no\nthere is not.\n\nDoes this seem like a hack? You bet. Do I wish I'd known about this, I dunno,\nsome time between 2010 when it seems like it was implemented in webkit? Um,\nyeah.\n\nGod damn it.\n\n",_path:"post/ukw6zqkqjtti"},"post/path=15-elements-of-composition":{keywords:[],date:"2024-09-20T17:33:48.145Z",summary:"These are the notes I took while watching Peter Evans' excellent YouTube video with the same title.",path:"15-elements-of-composition",author:"Tonio Loewald",_created:"2024-09-20T11:33:32.658Z",title:"15 Elements of Composition",_modified:"2024-11-15T21:13:22.459Z",content:"\n\nI came across a nice video entitled 15 Elements of Composition in Photography\nby Peter Evans, and I really liked it. It's free of fluff and has really nice examples.\n\nAnyway, if you don't want to watch the video, here are my notes, but the video is well worth a look.\n\nThe photograph above is one of mine, so don't blame Peter for that!\n\n## Vital\n\n- Focal Point (hook)—can be more than one, but one will be primary\n- Framing (as much in camera as possible, or crop in post; ignore aspect ratio, just make it look good)\n- Decide what you want to see and not to see (decide before you bring the camera to your eye)\n- Avoid background clutter\n- Resist temptation to “get everything in”\n\n## Non-Vital\n\n- Rule of Thirds (definitely not vital, but don’t get in the habit of centering your primary point of interest)\n- Leads-ins (usually diagnonal lines or curves that create depth) and foreground objects (don’t need to be obvious)\n- Diagonals, triangles, and curves (can be implied)\n- Keep horizons level\n- Give living creatures (people or animals) “looking/moving space” (also facing). This can even apply to inanimate objects like flowers and vehicles.\n- Rules of threes (or odds)—people weirdly prefer looking at three things or, failing that, odd numbers of things. \n- Positive & Negative Space (subjects of importance vs everything else)—try for a pleasing balance\n- Color, Shape, and Form\n- Strong Color (if related to the point of interest) can help, otherwise it is likely distracting\n- Frame within a frame (overused)\n",_path:"post/q6pw2t0a4y5j"}}