HNHacker News
TopNewBestAskShowJobs

fleabitdev

422 karma · joined June 12, 2020

submissionscomments
fleabitdev··on Rendering Crispy Text on the GPU
Getting rid of subpixel AA would be a huge simplification, but quite a lot of desktop users are still on low-DPI monitors. The Firefox hardware survey [1] reports that 16% of users have a display resolution of 1366x768.

This isn't just legacy hardware; 96dpi monitors and notebooks are still being produced today.

[1]: https://data.firefox.com/dashboard/hardware

fleabitdev··on Pope Francis has died
Yes - I think it caught my attention because it was such a mystery. It was a welcome thing to hear from one of the most powerful people in the world, but it came like a bolt from the blue. As far as I know, he never revisited the topic.
fleabitdev··on Pope Francis has died
Last year, an interviewer asked Francis how he envisages hell. His response stayed with me: “It’s difficult to imagine it. What I would say is not a dogma of faith, but my personal thought: I like to think hell is empty; I hope it is.”
fleabitdev··on Four Years of Jai (2024)
Sorry, I wasn't very clear - I think that using an object pool in a GCed language is like writing code in a dialect of that language which has no allocator.
fleabitdev··on Four Years of Jai (2024)
It's difficult to design a language which has good usability both with and without a GC. Can users create a reference which points to the interior of an object? Does the standard library allocate? Can the language implement useful features like move semantics and destructors, when GCed objects have an indefinite lifetime?

You'd almost end up with two languages in one. It would be interesting to see a language fully embrace that, with fast/slow language dialects which have very good interoperability. The complexity cost would be high, but if the alternative is learning two languages rather than one...

fleabitdev··on Cowboys and Drones: two modes of operation for small business
When half of a team are lone wolves because they see no other way to get things done, and the other half are rules lawyers who don't care whether things get done, I think that's a clear sign of management failure. Both of these personality types show up when team members have no faith that they can efficiently work together with their teammates: "drones" reject the idea of working efficiently, and "cowboys" reject the idea of working together.

There are lots of ways for management to go wrong, but if you feel like this article describes your small business, here are some low-hanging fruit:

- Have you sought out management coaching, or are you trying to become a good manager by trial and error?

- Do you treat your employees as trusted, respected professionals (meaning: people who might know better than you)? Do your employees treat one another that way? Do they give you that same respect?

- When you've made a mistake, big or small, how do you discover it? Do your employees and co-founders feel safe and secure enough to give you frequent negative feedback? Do they trust that you'll act on it?

- Do you have the time and resources to provide good management for all of the employees that you're directly responsible for? Have you been properly hiring, promoting and delegating to spread out that workload as the team grows?

- Leaders are just team members whose job is to produce decisions, in the same way that a software engineer's job is to produce code. Are you actually doing your job, by consistently producing high-quality decisions? Who's keeping track of that?

- Your most impactful responsibilities are hiring, firing, promotions, setting salaries, and choosing how to balance quality against speed. Are you giving all of those decisions the care and effort which they deserve?

fleabitdev··on Is there such a thing as a web-safe font?
To provide fonts for N different scripts would multiply the file size by roughly N, and there are a lot of scripts in common use:

https://en.wikipedia.org/wiki/List_of_writing_systems#/media...

Variable fonts would help, but I don't think it would be enough, especially if the goal is to provide a wide selection of fonts.

fleabitdev··on Is there such a thing as a web-safe font?
It's a nice thought, but internationalisation would make that difficult. On my system, the Noto international font weighs about 170 MiB compressed (gzip-compressed TTF, sans and serif styles, all weights, including symbols and colour emoji).

Even just focusing on Latin scripts, a few hundred fonts would roughly double the compressed download size for browsers and Electron apps.

It might be possible to come up with a better compression scheme which exploits redundancy between different typefaces, but as far as I know, that tech doesn't exist yet.

fleabitdev··on How to draw an outline in a video game
One day, I'd love to dive into stylised 3D graphics as an R&D project. There's been decent progress recently, but I think there's a lot of low-hanging fruit left to pick.

Some open questions:

- How do you reduce the detail of a toon-rendered 3D model as the camera zooms out? How do you seamlessly transition between its more-stylised and less-stylised appearance?

- Hand-drawn 2D animations often have watercolour backgrounds. Can we convincingly render 3D scenery as a watercolour painting? How can we smoothly animate things like brush-strokes and paper texture in screen space?

- How should a stylised 3D game portray smoke, flames, trees, grass, mud, rainfall, fur, water...?

- Hand-drawn 2D animations (and some recent 3D animations) can be physically incorrect: the artist may subtly reshape the "model" to make it look better from the current camera angle. In a game with a freely-moving camera, could we automate that?

- When dealing with a stylised 3D renderer, what would the ideal "mesh editor" and "scenery editor" programs look like? Do those assets need to have a physically-correct 3D surface and 3D armature, or could they be defined in a more vague, abstract way?

- Would it be possible to render retro pixel art from a simple 3D model? If so, could we use this to make a procedurally-generated 2D game?

- Could we use stylisation to make a 3D game world feel more physically correct? For example, when two meshes accidentally intersect, could we make that intersection less obvious to the viewer?

There are probably enough questions there to fill ten careers, but I suppose that's a good thing!

fleabitdev··on Boardgame.io: an engine for creating turn-based games using JavaScript
https://longwelwind.net/blog/networking-turn-based-game/
fleabitdev··on Boardgame.io: an engine for creating turn-based games using JavaScript
boardgame.io only runs game logic on the server, and it censors the State just before sending it to each client. This strategy makes the UI feel less responsive, but it keeps things simple.

The Swords and Ravens blog post recommends resolving actions on the client when they don't require secret information, but resolving other actions on the server. You'd also need to resolve actions on the server when they involve RNG.

fleabitdev··on Boardgame.io: an engine for creating turn-based games using JavaScript
That's the exact approach I'm considering for the new engine I mentioned!

Although that strategy enables you to store and recover the state of a game, it doesn't give you the ability to inspect a snapshot of that state. How can you print the card which has just been played, if that data only exists as an argument in the call stack of a suspended async function? In the same way that you can't inspect the local variables captured by a closure, mainstream languages also provide no way to inspect a suspended stack frame.

This problem interferes with debugging, consistency checks (e.g. hashing the game state to check that two clients are in sync), and unit testing.

fleabitdev··on Boardgame.io: an engine for creating turn-based games using JavaScript
This engine uses a Redux-like architecture. You have a State type (containing data like "the position of the black kingside rook") and a stream of in-game actions (like "knight to F3"). Each action is handled by a pure function which converts the current State to a new State. You can either transmit State deltas from the server to the client, or just transmit the actions themselves (https://longwelwind.net/blog/networking-turn-based-game/).

This design makes it easy to implement optimistic updates, rollback, replays, automated testing, and recovery after a disconnection. It's a surprisingly good fit for UI, too; you can render simple games as a React component which takes the current State as one of its props.

However, a stream of context-free actions can be a really inconvenient representation for some games. The rules of a board game are often like the control flow of a computer program: you'll see branching, iteration, local variables, function calls, structured concurrency, and sometimes even race conditions and reentrancy. When you try to represent all of this logic as a State object, you're basically maintaining a snapshot of a "call stack" as plain data, and manually resuming that "program" whenever you handle an action. It doesn't seem ideal.

I've been sketching a board game engine which would represent the game logic as normal code instead. It seems promising, but it really needs a couple of language features which don't exist in the mainstream yet, like serialisation of suspended async functions.

fleabitdev··on If not React, then what?
The problem described by antris is that, if a developer were to naively tear down and rebuild the entire DOM tree on each state change, the browser would discard some important state which belongs to the old DOM nodes. This state includes the text selection, keyboard focus, mouse capture, scroll position, and the progress of CSS transitions and animations.

React solves this problem by building a virtual DOM, and then conservatively updating the actual DOM (sometimes just mutating individual HTML attributes!) so that this state is preserved.

fleabitdev··on If not React, then what?
You're completely right, but I think it's worth adding that the O(n^2) to O(n) change isn't specific to React or UI. That same improvement is often seen when migrating other code from a mutating style to a pure-functional style.

"A pure function which transforms the entire input into the entire output" is obviously the simplest possible architecture for many programs, but people hesitate to use that architecture because of performance concerns. In practice, the baseline performance is often faster than they expect, and it can be made much, much faster using strategies like memoisation and fine-grained reactivity.

fleabitdev··on Can humans say the largest prime number before we find the next one?
I last read that story nearly a decade ago, I think. This time around, it feels more real. The engineers follow silly instructions from people with money, and then...
fleabitdev··on What Color is Your Function? (2015)
Sorry, the Wikipedia article isn't very helpful. Your example is the correct one.

The killer feature is that you can shadow a dynamic variable, changing its value for the duration of a dynamic scope, in the same way that an inner `catch` handler overrides an outer one.

For example, you could have a dynamic variable which tells the `print` function where to send its text output. The variable's value would probably default to stdout. By temporarily assigning a string-stream to that variable, you could collect an arbitrary function's text output into a string instead. Regardless of where the text is going, any function which produces text could still use the convenient global `print` function.

fleabitdev··on What Color is Your Function? (2015)
The term is "dynamic scope": https://en.wikipedia.org/wiki/Scope_(computer_science)#Lexic...

It isn't a very common language feature, but you could take a look at React's `Context` or Common Lisp's `defparameter`.

fleabitdev··on What Color is Your Function? (2015)
In a language with algebraic effects, questions like "how should I make this `CancellationToken` available to these forty small functions?" simply go away :-)

It's ambient state, just like a `catch` block, and so you can make it ambiently available to a whole tree of function calls. The outcome is the same as passing around a context argument by hand, and it can have equally strong type-checking, but the compiler handles all of the boilerplate for you.

To answer your second question: I agree that, in a dynamic language like JavaScript, making all of these language features implicit could be too costly. I'm more optimistic about effect systems in statically-typed languages.

fleabitdev··on What Color is Your Function? (2015)
I've spent some time thinking about language design recently. Coloured functions have a surprising number of downsides:

- They're sometimes much too explicit. When writing a complicated generator, I don't necessarily want to annotate every call to a sub-generator with `yield*`, especially if I need to drill that annotation through wrapper functions which aren't really generators themselves.

- Colours show up everywhere. If you have several functions with a `context: GodObject` parameter, then that's a function colour. Most third-party code will be unable to forward a `context` argument to a callback, so you'll have to manually smuggle it in using closures instead.

- Different "colour channels" don't compose nicely with one another. Even though JavaScript ES2017 provided both async functions and generators, ES2018 had to add multiple new pieces of syntax to permit async generators.

- It's normally impossible to write code which is generic over a function's colours. For example, if you have `function callTwice(f)` in JavaScript, you'd need a separate `async function callTwiceAsync(f)`, and a `function* iterateTwice(iterable)`, and an `async function* iterateTwiceAsync(iterable)`, despite the fact that all of those functions are doing the same thing.

Several small languages are experimenting with algebraic effect systems [1], which would make all functions colourless. If JavaScript had this feature, the syntax for defining and calling functions would be the same, no matter whether you're dealing with a normal function, a generator, or an async function. No more `async`, `await`, `function*`, `yield`, `yield*`, `for-of`, `for await`...

This can make everything too implicit and vague. The state of the art is for algebraic effect systems to be implemented in a statically-typed language, which then uses type inference to quietly tag all function types with their colours. This means that the compiler can enforce requirements like "you can only call generators within a scope which is prepared to collect their results", but that rule wouldn't prevent you from wrapping a generator in `callTwice`.

[1]: https://github.com/ocaml-multicore/ocaml-effects-tutorial

fleabitdev··on Writing GUI apps for Windows is painful
You're right that reactive UI is a poor fit for most languages (especially JavaScript!), but I think the problem is more general than that.

Good UI architecture needs some convenient and efficient way to propagate state changes between different parts of the UI framework [0]. This requirement sits in an awkward place, halfway between imperative programming and functional programming. It just isn't in the day-to-day vocabulary of any mainstream language, not even modern imperative languages which have a bit of functional programming mixed in.

I don't think OOP is any better at fulfilling this requirement. Being able to offload half of your program into a visual editor is nice, but it's cold comfort if the other half of your program ends up being a tangled mess of callbacks and data binding.

[0]: https://raphlinus.github.io/ui/druid/2019/11/22/reactive-ui....

fleabitdev··on Writing GUI apps for Windows is painful
Unlike Windows, Apple and Android have managed to publish modern UI libraries for their own platforms - but in both cases, this involved migrating the whole platform to a new programming language (Swift and Kotlin, respectively). If I remember right, C# also has a few features which were only added to make Windows Forms and WPF more ergonomic.

I'm starting to suspect that general-purpose languages just aren't flexible enough to keep up with changing fashions in UI development. Whenever somebody comes up with a new UI paradigm, you're not going to be able to take full advantage of it until somebody designs a new programming language with that paradigm in mind.

If so, this might explain why the Windows team keeps pushing new desktop UI libraries, only to abandon them within a few years - they just don't have the willingness or resources to migrate away from C# and C++, and so the quality of their UI libraries is stuck in 2006.

fleabitdev··on Writing Gnome Apps with Swift
I also admire the simplicity of this approach, but it has several downsides:

- Code-based UIs tend to work better with existing tooling, like comments and source control. You can freely customise your development environment, rather than being at the mercy of a single GUI app.

- Most serious UIs need code-like features, such as "for-each" to render a list of objects, or "if" to reveal a form when a checkbox is ticked. The easiest way to access code-like features is to write your UIs in code.

- You'll need to write some backing code in any case. Defining the UI tree and its "code-behind" in the same file could be considered more DRY.

- Live preview (or alternatively, hot reloading) will give you very quick iteration when writing a UI in code. It's not quite as good as drag-to-resize, but it's close.

- Automatic layout (which is non-optional nowadays) isn't necessarily a great fit for WYSIWYG editing.

- As React has demonstrated, the ability to quickly throw together custom components is extremely useful. Visual editors tend to make it more complicated and bureaucratic to define custom components.

fleabitdev··on UI = f(statesⁿ)
I finally grokked `view = fn(state)` once I understood that the `state` argument encompasses all possible inputs. Your global store, each component's internal state, the text selection, the mouse cursor position, the user's cookies, the progress of an animation - everything listed in the original blog post is just state, and the `fn()` doesn't necessarily need to care where it comes from.

The reason this is counterintuitive is that UI frameworks insulate you from some types of state. When you write your own `fn()`, it's only receiving part of the state, and it's only defining part of the view. In the browser, animations are a good example of this; other examples include keyboard focus, the size of the browser window, and most of the behaviour of native form controls.

fleabitdev··on UI = f(statesⁿ)
The purpose of `view = fn(state)` is to protect you from O(n*m) complexity scaling if you handle each event in isolation.

For any given part of your UI, you'll have n events to handle ("UI started up for the first time", "x changed", "y changed", "checkbox toggled", "undo", "redo", "file loaded"), and m invariants to uphold ("while the checkbox is checked, the user cannot edit y", "while x is less than y, the UI should display a warning", "this label displays the current value of y as a localised string"). If you try to manually maintain each invariant when handling each event, you'll find it works for simple cases, but it falls apart for larger values of n and m.

fleabitdev··on Oxide Hiring Process
Sensitivity and specificity [0] seem relevant here.

Every interview stage which is designed to reject bad candidates (true negatives) will also accidentally reject some good candidates (false negatives). The more eagerly you filter out bad candidates, the more you'll also filter out good candidates; sensitivity decreases as specificity increases.

This means that building more pass/fail tests into your interview pipeline may produce worse results - even if you have an enormous number of applicants, all with infinite time and patience! At each interview stage, there's a risk of accidentally rejecting the best remaining applicant, the one who would have outperformed all others if they were hired. For example, the article mentions "values mismatch" as a particularly good reason to hard-reject a candidate, but several of the company's values seem like they ought to be optional. Surely some truly excellent software developers lack courage, humour, and thriftiness? Could those qualities just as well be taught on-the-job? Could a non-courageous hire have useful things to teach you about caution?

Good hiring processes should draw a clear distinction between "must-have" qualities and "nice-to-have" qualities. Beyond a certain point, the problem stops being "filter out bad candidates", and starts being "out of several great candidates, make sure we choose the best one"; to solve those two problems, different tools are required.

[0] https://en.wikipedia.org/wiki/Sensitivity_and_specificity

fleabitdev··on Pixel Art: Common Mistakes (2020)
Nearest-neighbour, low-resolution rotation produces unpleasant results when the sprite contains fine details with a thickness of one or two pixels, e.g. outlines: https://imgur.com/a/UiAZ49z

Artistic preferences are subjective, but I expect most players would find that rotated sprite unappealing. This is especially true when the rotation is animated - the aliasing causes a distracting, staticky, random-noise effect which gives the impression that the sprite's fine details are chaotically changing. It's almost the aesthetic opposite of what most pixel artists are aiming for.

fleabitdev··on Pixel Art: Common Mistakes (2020)
Can you recommend any games in the first category?

I surveyed the indie-game landscape a couple of years back, specifically looking for games in your first category, but struggled to find any recent examples. 2D transforms, alpha-blending and lighting effects seem to be the standard now.

fleabitdev··on Pixel Art: Common Mistakes (2020)
I share your preferences, but it's a rock and a hard place. Performing any of the tasks you listed by hand is extremely labour-intensive. Doing without those features is a harsh creative limitation - in my experience, the result isn't "this game feels retro", but instead "this game feels oddly flat, repetitive and static", which is much less forgivable.

I've worked on a game which aimed for authentically retro pixel art, but in hindsight I think it was a silly misallocation of resources. There's a reason it's so uncommon nowadays. If I were to try again, I'd at least include high-resolution 2D transforms in the engine, and possibly dynamic lighting too.

I briefly explored the possibility of developing a style-preserving realtime rotation algorithm for sprites, but it's a hard problem.

fleabitdev··on Is Elden Ring that hard? Well, it depends what you mean by “hard”
Careful difficulty tuning is essential for fun gameplay. If an open-world game provides traditional exponential power and difficulty curves, and the game fails to guide you to the right content at the right time, you'll end up facing challenges which are too easy or too hard to be fun. Hollow Knight navigated this problem quite skillfully, but even in that game, I ended up effortlessly skimming through a few areas because I happened to leave them too late. What a waste of hours of content!

I'd like to see more open-world games experiment with a flat power curve: simply throw away the idea that the player character's numerical power should increase as the game goes on. If Super Mario World had increased the player's jump height or hit points based on the number of levels completed, it would have made the game much worse - so why do we tolerate the same thing from Castlevania and Final Fantasy?

Players might miss the (artificial) feeling of mastery, but I don't think it's worth the cost. Finding better ways to achieve that same feeling would be an interesting design challenge!

← PreviousPage 2 of 3Next →