3,377 karma · joined March 7, 2007
Web: https://brianbeck.com/
Mastodon: https://hachyderm.io/@exogen
The cursor size adjustment is mentioned further down on the page.
This was going to be my suggestion because it also fixed a similar CPU/GPU related issue many years ago: Apple's own TV.app would have minuscule color handling differences whenever subtitles would show during a movie. This was driving me nuts while showing a moody black & white film for a movie night - every time a subtitle would pop up, the entire scene's black levels would shift slightly (and it wasn't any kind of adaptive/localized brightness or anything like that, it was the actual rendering).
Some online sleuthing revealed it was GPU related (pure GPU video decoding vs. the CPU overlaying subtitles on the screen), and that bumping up the cursor size (even the tiniest amount) in mouse settings would fix it. It worked.
It's barely noticeable, but I actually prefer the slightly bigger mouse cursor now anyway, so it's part of my standard macOS setup.
Because that third-party code adds its own CSS.
Because my HTML and the third-party HTML live in the same document. Thus, my CSS could target their HTML, and their CSS could target my HTML. There are no modules and therefore no module boundaries.
Because my CSS and the third-party CSS share a global namespace. Thus, their CSS selectors might overlap with my CSS selectors. Hopefully nobody called anything ".container" or ".wrapper" or ".button" since there's no namespacing, right?
Keep in mind these components might be arbitrarily nestable. Maybe my elements can be a child of theirs? Maybe theirs can be a child of mine? Can I opt out of the cascade? Should I?
Maybe we could all use the Shadow DOM? I don't really have control over what third-parties do, but let's say I dig into their implementation (what fun) and choose only ones that use the Shadow DOM. Now I've massively bumped up the complexity and ensured my document can't render without JavaScript; lots of user-agents go right out the window and see nothing.
Let's reduce it to the simplest possible case: you and I are working on the same website, but I'm responsible for the header and you're responsible for the footer. We even have our own separate .css files. Yay, no merge conflicts.
Can you and I safely write our selectors without coordinating with each other?
(If you think the answer is yes... you're wrong.)
Now add a few dozen more team members and several third-parties who we in fact CANNOT coordinate with.
Everywhere you turn there are tradeoffs and half-solutions. Turns out "Just Do X" is not very helpful. Thus, I don't blame people at all for developing their own solutions to scaling CSS - like OP's framework.
There are still no affordances for many things, so people have to invent their own solutions.
That misses the point. They're identifiers scoped to the entire document - the same way global variables and global functions are identifiers scoped to an entire program.
One of my earliest webdev jobs was at a company whose product was a widget you could add to your site by adding our `<script>` tag. Thus, our CSS needed to coexist with the first-party site's, not to mention any other third-party widgets on there. In other words: the same exact reason you need modules in traditional languages.
Namely:
- Selectors are a global namespace. Imagine if every variable and function in your favorite programming languages were global and so had to be unique. No modules or namespaces. Developing a system to fix that would be pretty high on my list of priorities... coming up with a cool solution and then people telling me to "just learn the language" would be pretty fucking infuriating, don't you think?
- Several fighting priority systems (did you know about newer ones like @layer?). And equivalent priority falls back to source order - OK, so how do you square that with dynamic loading? Some navigation paths through an app would inject some CSS first whereas others would inject other CSS first. Good luck!
So, if that's not good enough for you, what would an even more faithful immediate mode API even look like? (And if your idea includes ditching the DOM as the render target, which is inherently "retained mode" and could never be otherwise due to performance and DOM state (like focus), I'm afraid it's a nonstarter.)
No.
> That still leaves the door open for XSS.
The door for that in React is called `dangerouslySetInnerHTML`, but it's extremely rarely used.
> jsx needs to rename HTML attributes, because of overlap with JS keywords like "class"
That's not really inherent to JSX, just React's use of it. SolidJS, for example, uses `class` instead. But in any case – JSX didn't make up those names. Those are the property names on JavaScript's DOM classes. The fact that there's confusion between "attributes" and "properties" is pretty baked-in to the Web platform, even causing confusion in standard Web Components. Every DOM library and framework (even jQuery) has needed to decide whether it's operating on properties or attributes.
const div = document.createElement('div');
div.className = 'foo';
> It also comes with hacks like "<>" or fragment.The DOM has the same concept, DocumentFragment. How else would you represent e.g. "two sibling nodes with no parent node"?
> It lacks the awareness of context and therefore is not truly like HTML.
On the contrary, I'd argue it has way more context. It knows, and will warn you, if you try to do any DOM element nesting that the HTML spec forbids, for example.
> can be structurally pattern matched upon and iterated through, like a proper tree structure.
You are literally describing the output of JSX. Glad you like it ;)
I also don't get why "XML-like syntax in the JS code" is even a point worth complaining about. If anything, we should be encouraging people to experiment with more DSLs for building UIs. Make your tools suit the task. Who the fuck cares, if it's clear and productive?
Some of the custom features you may be referring to I implemented as custom properties in the glTF output - like surface flags. "Outside Visible" is one example, it's a flag baked into each .dif surface that determines whether rays can reach it from the outside, so the engine knows whether to apply the map's directional sunlight, or just ambient and light map lighting. So, even though it technically could try to render with modern PBR, dynamic lighting/shadows and all that, it instead renders as close to the original as possible using the same (or similar) techniques. Comparing screenshots with actual Tribes 2 renders is often indistinguishable unless you really know what to look for!
Old games like this have a small (and shrinking) audience of people who care about them. With Tribes 2, for example, there are only ~50 people who actively play on a regular basis. A subset of those people are programmers, and a subset of those have the time & energy to put into a project like t2-mapper, assuming they're even interested. I got a basic version working, but then Claude Code helped decode and convert obsolete Dynamix/Torque3D file formats (improving existing Blender addons that were incomplete), got TorqueScript running in the browser, wrote shaders, and generally helped figure out what the original C++ code was doing.
In the past, you'd need the stars to perfectly align for stuff like this to happen: a passionate super-fan with the time, resources, knowledge, and persistence to see it through. Now, you mostly just need the persistence (and maybe a couple hundred bucks for tokens). I foresee people with niche interests (but not necessarily a programmer's skillset) being able to extend the lifetime (and maybe audience) of their obscure or obsolete software.
My experience here is also related to music. Here are some cases to think about:
What's the actual title of the song "Mambo #5" vs. how you might search for it or find it referenced in other records? Mambo #5? Mambo No. 5? Mambo No. Five? Mambo Number 5? Mambo Number Five? And that's not even getting to the fact that the actual title is actually longer, with a parenthetical. This is a case where bigrams, trigrams, or other string similarly metrics wouldn't perform very well. Same with the Beatles song, is it "Dr. Robert" or "Doctor Robert"? Most string similarly algorithms put "Dr" and "Doctor" pretty far apart, but with vectors they should be practically equivalent.
How about "You've Lost that Loving Feeling"? Aren't there some dropped Gs in those gerunds? Is it You've Lost That Lovin' Feeling? You've Lost That Lovin' Feelin'? You've Lost That Loving Feelin'? In this case, string similarity (including trigrams) perform very well.
How about songs with censored titles? Some records will certainly have profanity censored, but would it be like "F*ck", "F**k", "F@$k", or what? And is the censorship actually part of the canonical song title, or just some references to it?
In the "#5" and "Dr." cases, this could be solved pretty effectively by the normalization step described in the article (hardcoding what #, No., and Dr. expand to) – although even that can get pretty complicated: what do you do about numbers? Do you normalize every numerical reference, e.g. "10 Thousand", to digits, or words? What about rarely used abbreviations, or cases where an abbreviation is ambiguous and could mean different things in different contexts? If someone has a song called "PT Cruiser" are you gonna accidentally normalize that to "Part Cruiser"? For this reason, I like to see this not as a "normalization" step, where there's a single normalized form, but rather a "query expansion" step – generate all the possible permutations, and those are your actual comparison strings.
It seems like embeddings could do the job of automatically considering different spellings/abbreviations of words as equivalent. I'm just a casual observer here, but I'm sure this is also a well-explored topic in speech-to-text, since you have to convert someone's utterances to match actual entity names, like movie titles for example.
(On that note, many complaints about Safari I hear from developers fall on my ears as "I don't care about web compatibility!" as it has never NOT been the case on the web that you need to care about feature support and resource management.)
To satisfy the urge of doing something else ambitious in the browser, I'm now doing the same thing for Tribes 2 maps: trying to make a web-based map viewer and editor: https://exogen.github.io/t2-mapper/ (editing/creation part still in progress)
I got this working for most maps pretty quickly. It translates the mission object tree from the Torque .mis files into a Three.js scene graph. Eventually though, I noticed that some mission definitions were more dynamic – Torque .mis files are really just TorqueScript .cs files with a different extension and some pragma/magic comments. So, to actually handle every map would require not just a mission file parser, but a whole TorqueScript runtime. Implementing THAT part seemed really tedious and, frankly, uninteresting to me. So I had Claude Code get a whole TorqueScript transpiler and runtime working. Now, when you load a mission, it actually runs all the same scripts that Tribes 2 runs to load the mission, all the way from server.cs and its `CreateServer()` function.
Currently, I'm continuing to get its rendering matching Tribes 2 as closely as possible, and setting things up so that live editing of missions will work.
Before IE became Edge (and maybe even in the earliest versions of Edge), there were certain styles and descendants that simply did not work on a <button> element, like Flexbox and Grid positioning. So, if your button had content like an icon, and you were trying to align it a certain way with the label, you simply couldn't use some features of CSS like you could with a <div>. It was a pain in the ass.
In the same vein, do you remember the period where some browsers wouldn't allow you to make a button look like a link using CSS, because they thought it might deceive people and thus be a security issue? I do.
And similarly when people complain about the complexities of webpack and bundlers in general, do you remember including the jQuery <script> tag on the page and then almost always needing to call `jQuery.noConflict()`? And how in those days, most people got even THAT wrong, because atomic <script async onload> behavior didn't work correctly in all browsers yet, so other code could actually run in between a <script> and its onload callback, meaning the jQuery.noConflict call was ineffective and something else could steal it? I remember. webpack fixed that by automatically scoping everything.
Nowadays, a lot of those workarounds are unnecessary (depends what browsers you're supporting). But it's not like there was never a reason for them.
Or a more complicated app: https://exogen.github.io/t2-model-skinner/
Are all of Next.js’ features overkill for such sites? Sure, but the convenience such frameworks provide is worth it. And the reason to prefer it over something like Vite is simply routing, which Vite doesn’t cover out of the box, so as soon as I want to add a second page, I now have another problem to solve.
Next.js’ best feature is simply that you’re up and running with `npm i react react-dom next` and step two is just writing the pages.
Location: Seattle, WA
Remote: preferred
Willing to relocate: no
Technologies: TypeScript, Python, React, Next.js, Django, Postgres
Resume: upon request
Email: exogen@gmail.com
Your classic web and open source hacker with over 20 years of experience, most fluent in JS/TS and Python. I've tackled a wide variety of interesting problems and domains, e.g. graph isomorphism, query expansion, geocoding, dev tools, performance, data visualization, API design, server-side rendering, entity matching/record linkage, web scraping, games, reverse engineering binary formats, typography, codebase migrations, and more. Both startup and big-corp experience, but I prefer working with small teams.Ask me about the time a stolen bike led to creating an arbitrary-precision math library and discovering a bug in Mathemetica...
(Aside from that: not a fan, it makes the chrome of the video player more distracting.)
ca. 2017 I undertook the considerable task of building a GraphQL interface to MusicBrainz, to support a side project of mine. This was a great experience for learning the breadth of MusicBrainz and how to design things with GraphQL. Sometimes I look at the documentation generated from the resulting schema and wonder when I ever had that much time: https://github.com/exogen/graphbrainz/blob/master/docs/types...
OP: in case you weren't aware, websites that do this have been facing legal challenges recently, with some being shut down. I suspect that's part of the reason there's room in the market for yours... while it lasts :(
A few months ago I also painstakingly went through and added tags and comments to many of these files. Later, when the network drive was remounted, the comments were all wiped out while the tags remained (presumably, because of the different attribute storage mechanisms the different fields use, combined with the changing mount ID). Maybe that's recoverable somehow, but I simply don't have the patience. To me, it's as good as lost. Thanks, macOS!
On several occasions I would overdraw my debit account, notice the issue and deposit money, and then continue to make purchases with the newly available funds. Despite having sufficient funds, at the end of each day, Fifth Third Bank would reorder the day's transactions to maximize the overdraft penalties, as if all of them had been settled while overdrawn. Their customer support team would pretend this was actually beneficial to customers and launch into a nonsensical explanation where one of the terms involved was, literally, "the matrix." So you'd be sitting on the phone in disbelief as they talked about "the matrix."
Anyway, there was eventually a class action lawsuit over it. Ever since then, my inclination is to regulate the hell out of these sleazy institutions. They clearly can't be trusted to act in good faith.