I led a team that successfully build a large web application with multiple components in Elm. We eventually made the decision to rewrite it in React and Typescript after the 0.19 release. There were two things driving that decision:
The first was that it was difficult to sustain a small team in a bigger organization that had settled on JS and React. It meant team members couldn’t easily move to other teams if they wanted a change of pace or different responsibilities. And, likewise, we couldn’t easily bring over people from other teams when we needed extra help. Hiring, too, was difficult. It was difficult to communicate to our recruiters what functional programming was, and that we didn’t need experience with Elm directly, but that experience with functional programming was helpful. On the other hand, the recruiters could easily feed the JS/React/Typescript funnel for other teams, and so they focused most of their effort there.
The second that the package management story was not great for a production system, especially after 0.19. Because everything had to come from the central repo, without any support for mirroring, it meant that any outage of that repo would break our builds. It also meant that if we wanted to develop our own utility libraries for our own use, the only way to reuse them was to copy the code around, since we couldn’t have a private package repo.
I really enjoyed working with Elm, and I much prefer it to Javascript even to this day. But I can’t recommend it for anything but personal hobby projects. I’m hoping one of the forks will get there eventually.
This definitely is not congruent with why I didn’t adopt Elm more deeply. To me, Elm was too limiting; it was ossified, locking in bad design decisions and making them impossible to revisit or even experiment with alternatives. There’s also the compilation model; I don’t want to invoke any ECMAScript-specific compilation workflow if I can avoid it, since they’re usually not hermetic and they want to generate lots of scaffolding code. As with many other users, the showstopping misfeature was the inability to add native ECMAScript to bridge to some portion of the browser API which Elm hadn’t yet wrapped or typed.
If we’re thinking retrospectively, I would also mention the mistake of tying a language exclusively to Web browsers. It is fine to support browsers as a platform, but I feel like Elm always treated non-browsers, including its development REPL, as second-class platforms.
Wouldn’t you say that puts you in the category of users who generally disagreed with the goals of Elm? In which case Elm was never going to be the language for you. Definitely a contributing factor to reduced adoption, because it turned you away to other options. I think there’s probably multiple sets of people when it comes to niche languages:
Users who aren’t interested in any part of the language
Users who are interested in some part of the language, but not the whole thing
2.1) Users who find the missing parts a deal breaker
2.2) Users who are fine with the missing parts
Users who are interested in the whole language
which then has overlap with:
a) Users who have their needs met by technologies they already use
b) Users who have their needs met but are curious in alternatives
c) Users who don’t have their needs met
This blog post mostly covered 2.2/3 b/c, but there’s definitely a lot to be said for the 1/2.1 a/b/c groups too.
I agreed strongly with the design goals of Elm: first-order data, total functions with static types, etc. Elm was one of several MLs that I evaluated because I didn’t want to write ECMAScript directly. I recall that I gave up on using Elm for a project because I needed to fetch and parse binary data from a URL, and there was neither a satisfactory way of doing it nor a way of adding it myself. This evaluation was several years ago, and I expect that my particular issue has now been overcome, but I can’t operate in high-control communities like that.
In contrast, I’ve had issues with PureScript and OCaml, and I deliberately avoid e.g. using spago, but I was able and allowed to write my own hacks and build scripts in order to satisfy both their tools and my design requirements.
I currently have a few hand-written ECMAScript stubs which use Preact, and then the bulk of the application code is written in Cammy and compiled first to PureScript and then to browser-friendly code. In the future, I plan to compile Cammy directly to WASM. I’m fully aware that it’s unreasonable to ask other people to write in Cammy, so I don’t.
Probably. The bibliography might be enlightening. Cammy is only meaningful in that it approximates the pseudocode seen in many category-theory papers; otherwise, it’s really not a good language for anything.
I used Elm for a project and regret it now. It ended up being equal parts Elm and TypeScript because some things were just too hard to do in Elm. Apart from the native-whitelist.json fiasco everyone is familiar with, there are a couple other things I dislike about Elm:
“No runtime exceptions” does not work in practice. It’s a bit like removing panic! from Rust and claiming you’ve solved all runtime exceptions. How would that actually work out? Result and Option would proliferate in function signatures. Every time you access an array you’d have to handle or propagate the out-of-bounds error. If you wrote a data structure that maintains runtime invariants, instead of asserting them, you’d have to leak their failure into the public interface. There were cases in my Elm project where I wanted to assert that a map had a certain key, but instead the language shepherded me to handle the Nothing case with some code that typechecked but would do nothing useful at runtime.
The compiler is too opinionated. For example, tuples cannot contain more than 3 elements. Why? Because four elements is too confusing! Some more zealous contributors wanted to limit it to 2 elements, or to extend it to custom data types. In general, Elm seems more interested in limiting what you can do (cheerfully! with exclamation marks!!) than addressing real world needs.
Elm hasn’t seen an update since 0.19.1, which came out in October 2019. That’s a pretty long time to go without updates, but if it works, what’s the point in doing releases for release sake? To some adopters, this can look pretty concerning - especially from the Javascript world where everything is being updated all the time. To other eyes, it might just simply represent stability.
It’s hard to take that seriously when it’s still 0.x. There are plenty of open bugs.
Elm hasn’t seen an update since 0.19.1, which came out in October 2019. That’s a pretty long time to go without updates, but if it works, what’s the point in doing releases for release sake? To some adopters, this can look pretty concerning - especially from the Javascript world where everything is being updated all the time. To other eyes, it might just simply represent stability.
If Elm is supposed to now be stable and reliable, why is it still on release 0.19.1 and not 1.0.0? Even if it is stable, it’s not promising stability.
The native module restriction is spot on the reason why I and everyone I know left Elm.
The Elm developers felt that native modules don’t matter. But behind the scenes people relied on them heavily. Entire projects. As projects got big, they very frequently needed native modules. And then overnight, Elm killed off that entire ecosystem for no reason. With no way back. And no way forward either. A lot of functionality was simply gone and the only answer was “one day we’ll have a library for this, just wait a few years, maybe”. If your projected needed that functionality there was literally nothing you could do.
We had to throw away a lot of work because of Elm. I’ve never seen a language show more contempt for the users who were most heavily invested in it. I would never go back, it’s a toy language today.
It’s a shame. Elm itself was great until they killed it. Now we’re all using typescript.
Native modules weren’t really used that much compared to the entire Elm ecosystem. That being said, I wrote and used them a lot, including writing the only docs on them at the time. Language restrictions likely played a part, as discussed - TypeScript provides a better experience of doing things which are difficult in Elm.
My reasons for picking TypeScript for new projects has less to do with “worse is better” and more to do with how the JS/TS ecosystem evolved. Sure, editor support has improved via language servers, but that’s an ever-smaller part of the equation. Doing an apples-to-apples comparison of language and type system features gets harder every day because of how much of the ecosystem you give up. I used Reason, PureScript, ClojureScript and others so much over the last decade and still working full-time on a Reason codebase, but how ubiquitous TypeScript has become is making it harder to choose the other languages every day.
This has to do with the heavier frameworks and SDKs of today. You can’t just bind to React, jQuery and Express and call it a day. These days you use bigger frameworks such as Next.js and Remix that come with their own types, filesystem conventions, build processes that are not compatible with “alt-js” languages. It is an enormous effort to recreate all these types, put all the hacks in place to fit their requirements and keep it all running while migrating between major versions.
On top of these frameworks, there are other high level tools such as Prisma, and Relay, platform SDKs such as Supabase, Temporal and AWS, massive libraries such as React Native, all of which come with a ton of APIs and generated TS types you would need to recreate or write bindings for. It is easy to get burned out trying to chase these while staying productive with a language like ReScript. If you are building a full-stack app these tools and platforms do help you ship applications faster. The constant battle between owning your entire stack and rewriting everything in Elm versus trying to write bindings to these massive APIs and keeping them up to date gets super tiring.
I think it used to be easier because both JavaScript ecosystem and TypeScript language were in a worse state. Having a good typed language with a real standard library was a boost on its own, and the platform API surface was much smaller. Today you build on top of multiple much larger platforms (Browser + Node/Deno/Workers + Vercel + Supabase + React + React Native + Next.js + …) and all of this assumes you use TypeScript already.
Using a different typed language that compiles to JS sometimes feels like trying to ship a Machine Learning SaaS product using Nano editor, writing a Lisp-to-Python compiled language that doesn’t let you use anything like Django, Tensorflow, Pytorch, Numpy etc. But at least you get macros.
I think there is still room for new languages to succeed on the web platform. And this doesn’t necessarily depend on WebAssembly. But these new languages should be designed to fit the JS module system better for easier framework interop, and they should allow importing TypeScript code without writing bindings (like Zig’s @cImport).
Elm strives to be a pure language - with no unmanaged side effects, and no runtime errors. In order to achieve that, native modules were restricted so that there couldn’t be code that would either have buggy JS or do secret side effect stuff. Previously there was a whitelist of packages with native modules allowed on the official package host, to cut down on the possible native modules. But as the community and needs grew, there were cases of people handrolling their own packaging solution to bypass the whitelist. Eventually, this whitelist was replaced by a hardcoded restriction within the compiler itself. The idea was to keep Elm pure and safe for all users, and to encourage solutions written directly in Elm rather than in some wrappers around JS.
This was seen as problematic as it prevented some libraries doing things with stuff like the DOM API. But I think the goal was to keep Elm as consistent as possible.
Overall with hindsight it turned out WebComponents are a great way to keep Elm pure and still have some of that JS whenever you need it, and if all else fails there are still ports (for async Elm<->JS communication) and Proxies (kind of a dirty hack you can smuggle on init via Flags if you really really need sync Elm<->JS, sacrificing purity/referential transparency).
I reckon if native modules were more widespread, the community would take much longer to arrive at WebComponents.
I don’t want to go into any of my history with Elm, but I hate that I have to agree with the author that TypeScript is the no-brainer with flaws. Many this is good enough and in a lot of spheres, despite the UI being where the clients interact with the product, there’s little to no emphasis on the front-end. The entire “innovation” budget (when people follow this sort of concept) is blown on Haskell for the server, and then the front-end in teams like these can end up these monstrous pieces of fp-ts or glue to try to make the front-end more Haskell without having to bite into a language that provides the proper abstractions and ergonomics (as well as the lack of some harmful features like inheritance, null, statefullness, etc.). The great part about Elm in the period post Signals was that other ecosystems didn’t offer the easy and the abstractions. TypeScript gets all the features eventually added to it, and now you can have your ADTs… but everything that could be 1 line is now 17 that you have to maintain and try to wade through while going against the, for lack of a better term, ‘normie’ TypeScript idioms. /rant
(fp-ts and effect-ts are quality projects made by smart folks, but I still hold that these aren’t the ergonomics that the folks coming to these libraries are looking for)
Offtopic, but who chose that colorscheme? Maybe I’m the only one with that issue, but after reading that post, my eyes have trouble focusing and I can barely see enough to type this comment. Use reasonable contrast, please. Full black vs full white is far too contrasty and is genuinely painful to look at. (Not exaggerating here)
First time I’ve heard this comment. It’s a default theme from Substack - which automatically chooses font color to match background color. That said, I’ve just switched the theme color to another one, so feel free to check it out again. There’s not much I can do beyond that sadly. Alternatively I suggest seeing an eye doctor if it caused you that much of a problem!
I love the contrast. It means I can keep the brightness down on my devices and save power while it being easy to read. On OLED gray on other gray, especially if tinted warm or cool, generally looks gross and artificial instead of the true blacks it can deliver.
May I recommend creating a custom ICC profile to tonemap towards more contrast?
Most OSes have a calibration utility to create such profiles, or you can use a proper calibration device. It’s always possible to increase contrast, but once shadows and highlights have clipped you can’t easily get them back.
My devices are 100% DCI-P3 and calibrated using a Colormunki + DisplayCAL Py3 + colord. It doesn’t change that text on OLEDs looks great as white/light gray on #000, and making a gray non-neutral, anything not HSL(0, 0%, $x), generally looks pretty ugly for reading until saturation gets high enough for you to say “this is navy” instead of “this is a blue-shifted gray”.
ICC profiles can also be used to increase contrast. You can e.g. create a profile that maps the range #222 to #eee to #000 to #fff. You can even chain multiple ones. That allows you to get clean white on black for content while allowing the rest of us to have a distinction between pixels that are off and a dark background. Reducing contrast on the other hand isn’t really possible, as once a value is clipped, you can’t distinguish between “is this meant to be the sun at #fff or is this just text with a clipped brightness?” (and neither can an ICC profile recover that)
I’ll keep my ICC profiles where they are for color-sensitive work. My userContent.css however if filled with turning websites into pure black background for better reading/power savings. I think a lot of folks might be running their brightness higher than is necessary in many scenarios; low-contrast themes are almost harder to read until you boost brightness. You can always set your prefers-contrast: less and I don’t think low contrast is a good default–especially with accessibility needs of those with low vision (there’s prefers-contrast: more, but high contrast is a different beast that goes into territory that I find distracting with no subtlety anywhere because that’s the point).
I’ll keep my ICC profiles where they are for color-sensitive work
And that’s the same for me. But that’s also why I need a high brightness. A standardized environment for color sensitive work requires 500 lux, per DIN. And you should adjust your contrast when designing websites for that exact environment. In fact, your monitor MUST be above 300nits if your room has daylight (per DIN)
Ah, well my contrast adjuster is to turn up the brightness as soon as I need to work with designs or photos, but keep it low for reading and for coding.
“Far too contrasty” is a somewhat baffling comment to me. It’s like, um, “this water is far too wet” or something.
Contrast is good for me. It helps my eyes. I am tired of, and by, trendy websites with muted pastels. Give me black on white or white on black, please.
I was genuinely in pain from this amount of contrast. Especially black on white is the worst kind of contrast. As I need accurate colors for work, I’ve got a very bright, high contrast OLED monitor, on which this amount of contrast is genuinely painful to look at (and should only be used for images to detail the contrast between the bright sun and dark shadows, not for text or graphics).
You can always increase contrast easily. But once highlights and shadows have been clipped, you can’t recover them. A LUT can easily map the range #222 to #eee to the range #000 to #fff, compressing all colors between #000 and #222 into #000. But you can’t do the reverse, once you’ve got #000, you don’t know what the original color was. You can easily change your monitor settings to artificially increase contrast or create an ICC profile. But I can’t do the same for recovering the lost dynamic range.
I led a team that successfully build a large web application with multiple components in Elm. We eventually made the decision to rewrite it in React and Typescript after the 0.19 release. There were two things driving that decision:
The first was that it was difficult to sustain a small team in a bigger organization that had settled on JS and React. It meant team members couldn’t easily move to other teams if they wanted a change of pace or different responsibilities. And, likewise, we couldn’t easily bring over people from other teams when we needed extra help. Hiring, too, was difficult. It was difficult to communicate to our recruiters what functional programming was, and that we didn’t need experience with Elm directly, but that experience with functional programming was helpful. On the other hand, the recruiters could easily feed the JS/React/Typescript funnel for other teams, and so they focused most of their effort there.
The second that the package management story was not great for a production system, especially after 0.19. Because everything had to come from the central repo, without any support for mirroring, it meant that any outage of that repo would break our builds. It also meant that if we wanted to develop our own utility libraries for our own use, the only way to reuse them was to copy the code around, since we couldn’t have a private package repo.
I really enjoyed working with Elm, and I much prefer it to Javascript even to this day. But I can’t recommend it for anything but personal hobby projects. I’m hoping one of the forks will get there eventually.
This definitely is not congruent with why I didn’t adopt Elm more deeply. To me, Elm was too limiting; it was ossified, locking in bad design decisions and making them impossible to revisit or even experiment with alternatives. There’s also the compilation model; I don’t want to invoke any ECMAScript-specific compilation workflow if I can avoid it, since they’re usually not hermetic and they want to generate lots of scaffolding code. As with many other users, the showstopping misfeature was the inability to add native ECMAScript to bridge to some portion of the browser API which Elm hadn’t yet wrapped or typed.
If we’re thinking retrospectively, I would also mention the mistake of tying a language exclusively to Web browsers. It is fine to support browsers as a platform, but I feel like Elm always treated non-browsers, including its development REPL, as second-class platforms.
Wouldn’t you say that puts you in the category of users who generally disagreed with the goals of Elm? In which case Elm was never going to be the language for you. Definitely a contributing factor to reduced adoption, because it turned you away to other options. I think there’s probably multiple sets of people when it comes to niche languages:
which then has overlap with:
a) Users who have their needs met by technologies they already use
b) Users who have their needs met but are curious in alternatives
c) Users who don’t have their needs met
This blog post mostly covered 2.2/3 b/c, but there’s definitely a lot to be said for the 1/2.1 a/b/c groups too.
I agreed strongly with the design goals of Elm: first-order data, total functions with static types, etc. Elm was one of several MLs that I evaluated because I didn’t want to write ECMAScript directly. I recall that I gave up on using Elm for a project because I needed to fetch and parse binary data from a URL, and there was neither a satisfactory way of doing it nor a way of adding it myself. This evaluation was several years ago, and I expect that my particular issue has now been overcome, but I can’t operate in high-control communities like that.
In contrast, I’ve had issues with PureScript and OCaml, and I deliberately avoid e.g. using
spago
, but I was able and allowed to write my own hacks and build scripts in order to satisfy both their tools and my design requirements.Out of curiosity, what do you currently use for client side web development?
I currently have a few hand-written ECMAScript stubs which use Preact, and then the bulk of the application code is written in Cammy and compiled first to PureScript and then to browser-friendly code. In the future, I plan to compile Cammy directly to WASM. I’m fully aware that it’s unreasonable to ask other people to write in Cammy, so I don’t.
Link: Cammy, a language designed by @Corbin
Is the Esolang page the only introduction?
Probably. The bibliography might be enlightening. Cammy is only meaningful in that it approximates the pseudocode seen in many category-theory papers; otherwise, it’s really not a good language for anything.
I used Elm for a project and regret it now. It ended up being equal parts Elm and TypeScript because some things were just too hard to do in Elm. Apart from the native-whitelist.json fiasco everyone is familiar with, there are a couple other things I dislike about Elm:
“No runtime exceptions” does not work in practice. It’s a bit like removing
panic!
from Rust and claiming you’ve solved all runtime exceptions. How would that actually work out?Result
andOption
would proliferate in function signatures. Every time you access an array you’d have to handle or propagate the out-of-bounds error. If you wrote a data structure that maintains runtime invariants, instead of asserting them, you’d have to leak their failure into the public interface. There were cases in my Elm project where I wanted to assert that a map had a certain key, but instead the language shepherded me to handle theNothing
case with some code that typechecked but would do nothing useful at runtime.The compiler is too opinionated. For example, tuples cannot contain more than 3 elements. Why? Because four elements is too confusing! Some more zealous contributors wanted to limit it to 2 elements, or to extend it to custom data types. In general, Elm seems more interested in limiting what you can do (cheerfully! with exclamation marks!!) than addressing real world needs.
It’s hard to take that seriously when it’s still 0.x. There are plenty of open bugs.
If Elm is supposed to now be stable and reliable, why is it still on release 0.19.1 and not 1.0.0? Even if it is stable, it’s not promising stability.
It sure seems like “abandonware”.
Would you really say the native module restriction had no play in the stalled growth?
The native module restriction is spot on the reason why I and everyone I know left Elm.
The Elm developers felt that native modules don’t matter. But behind the scenes people relied on them heavily. Entire projects. As projects got big, they very frequently needed native modules. And then overnight, Elm killed off that entire ecosystem for no reason. With no way back. And no way forward either. A lot of functionality was simply gone and the only answer was “one day we’ll have a library for this, just wait a few years, maybe”. If your projected needed that functionality there was literally nothing you could do.
We had to throw away a lot of work because of Elm. I’ve never seen a language show more contempt for the users who were most heavily invested in it. I would never go back, it’s a toy language today.
It’s a shame. Elm itself was great until they killed it. Now we’re all using typescript.
Native modules weren’t really used that much compared to the entire Elm ecosystem. That being said, I wrote and used them a lot, including writing the only docs on them at the time. Language restrictions likely played a part, as discussed - TypeScript provides a better experience of doing things which are difficult in Elm.
My reasons for picking TypeScript for new projects has less to do with “worse is better” and more to do with how the JS/TS ecosystem evolved. Sure, editor support has improved via language servers, but that’s an ever-smaller part of the equation. Doing an apples-to-apples comparison of language and type system features gets harder every day because of how much of the ecosystem you give up. I used Reason, PureScript, ClojureScript and others so much over the last decade and still working full-time on a Reason codebase, but how ubiquitous TypeScript has become is making it harder to choose the other languages every day.
This has to do with the heavier frameworks and SDKs of today. You can’t just bind to React, jQuery and Express and call it a day. These days you use bigger frameworks such as Next.js and Remix that come with their own types, filesystem conventions, build processes that are not compatible with “alt-js” languages. It is an enormous effort to recreate all these types, put all the hacks in place to fit their requirements and keep it all running while migrating between major versions.
On top of these frameworks, there are other high level tools such as Prisma, and Relay, platform SDKs such as Supabase, Temporal and AWS, massive libraries such as React Native, all of which come with a ton of APIs and generated TS types you would need to recreate or write bindings for. It is easy to get burned out trying to chase these while staying productive with a language like ReScript. If you are building a full-stack app these tools and platforms do help you ship applications faster. The constant battle between owning your entire stack and rewriting everything in Elm versus trying to write bindings to these massive APIs and keeping them up to date gets super tiring.
I think it used to be easier because both JavaScript ecosystem and TypeScript language were in a worse state. Having a good typed language with a real standard library was a boost on its own, and the platform API surface was much smaller. Today you build on top of multiple much larger platforms (Browser + Node/Deno/Workers + Vercel + Supabase + React + React Native + Next.js + …) and all of this assumes you use TypeScript already.
Using a different typed language that compiles to JS sometimes feels like trying to ship a Machine Learning SaaS product using Nano editor, writing a Lisp-to-Python compiled language that doesn’t let you use anything like Django, Tensorflow, Pytorch, Numpy etc. But at least you get macros.
I think there is still room for new languages to succeed on the web platform. And this doesn’t necessarily depend on WebAssembly. But these new languages should be designed to fit the JS module system better for easier framework interop, and they should allow importing TypeScript code without writing bindings (like Zig’s
@cImport
).I guess I just don’t understand why Elm restricted native modules to begin with. What was the stated reasoning?
Elm strives to be a pure language - with no unmanaged side effects, and no runtime errors. In order to achieve that, native modules were restricted so that there couldn’t be code that would either have buggy JS or do secret side effect stuff. Previously there was a whitelist of packages with native modules allowed on the official package host, to cut down on the possible native modules. But as the community and needs grew, there were cases of people handrolling their own packaging solution to bypass the whitelist. Eventually, this whitelist was replaced by a hardcoded restriction within the compiler itself. The idea was to keep Elm pure and safe for all users, and to encourage solutions written directly in Elm rather than in some wrappers around JS.
This was seen as problematic as it prevented some libraries doing things with stuff like the DOM API. But I think the goal was to keep Elm as consistent as possible.
Overall with hindsight it turned out WebComponents are a great way to keep Elm pure and still have some of that JS whenever you need it, and if all else fails there are still ports (for async Elm<->JS communication) and Proxies (kind of a dirty hack you can smuggle on init via Flags if you really really need sync Elm<->JS, sacrificing purity/referential transparency).
I reckon if native modules were more widespread, the community would take much longer to arrive at WebComponents.
I don’t want to go into any of my history with Elm, but I hate that I have to agree with the author that TypeScript is the no-brainer with flaws. Many this is good enough and in a lot of spheres, despite the UI being where the clients interact with the product, there’s little to no emphasis on the front-end. The entire “innovation” budget (when people follow this sort of concept) is blown on Haskell for the server, and then the front-end in teams like these can end up these monstrous pieces of
fp-ts
or glue to try to make the front-end more Haskell without having to bite into a language that provides the proper abstractions and ergonomics (as well as the lack of some harmful features like inheritance,null
, statefullness, etc.). The great part about Elm in the period post Signals was that other ecosystems didn’t offer the easy and the abstractions. TypeScript gets all the features eventually added to it, and now you can have your ADTs… but everything that could be 1 line is now 17 that you have to maintain and try to wade through while going against the, for lack of a better term, ‘normie’ TypeScript idioms. /rant(
fp-ts
andeffect-ts
are quality projects made by smart folks, but I still hold that these aren’t the ergonomics that the folks coming to these libraries are looking for)Offtopic, but who chose that colorscheme? Maybe I’m the only one with that issue, but after reading that post, my eyes have trouble focusing and I can barely see enough to type this comment. Use reasonable contrast, please. Full black vs full white is far too contrasty and is genuinely painful to look at. (Not exaggerating here)
First time I’ve heard this comment. It’s a default theme from Substack - which automatically chooses font color to match background color. That said, I’ve just switched the theme color to another one, so feel free to check it out again. There’s not much I can do beyond that sadly.
Alternatively I suggest seeing an eye doctor if it caused you that much of a problem!Edit: strikeout bad joke
I love the contrast. It means I can keep the brightness down on my devices and save power while it being easy to read. On OLED gray on other gray, especially if tinted warm or cool, generally looks gross and artificial instead of the true blacks it can deliver.
May I recommend creating a custom ICC profile to tonemap towards more contrast?
Most OSes have a calibration utility to create such profiles, or you can use a proper calibration device. It’s always possible to increase contrast, but once shadows and highlights have clipped you can’t easily get them back.
My devices are 100% DCI-P3 and calibrated using a Colormunki + DisplayCAL Py3 + colord. It doesn’t change that text on OLEDs looks great as white/light gray on #000, and making a gray non-neutral, anything not HSL(0, 0%, $x), generally looks pretty ugly for reading until saturation gets high enough for you to say “this is navy” instead of “this is a blue-shifted gray”.
ICC profiles can also be used to increase contrast. You can e.g. create a profile that maps the range #222 to #eee to #000 to #fff. You can even chain multiple ones. That allows you to get clean white on black for content while allowing the rest of us to have a distinction between pixels that are off and a dark background. Reducing contrast on the other hand isn’t really possible, as once a value is clipped, you can’t distinguish between “is this meant to be the sun at #fff or is this just text with a clipped brightness?” (and neither can an ICC profile recover that)
I’ll keep my ICC profiles where they are for color-sensitive work. My
userContent.css
however if filled with turning websites into pure black background for better reading/power savings. I think a lot of folks might be running their brightness higher than is necessary in many scenarios; low-contrast themes are almost harder to read until you boost brightness. You can always set yourprefers-contrast: less
and I don’t think low contrast is a good default–especially with accessibility needs of those with low vision (there’sprefers-contrast: more
, but high contrast is a different beast that goes into territory that I find distracting with no subtlety anywhere because that’s the point).And that’s the same for me. But that’s also why I need a high brightness. A standardized environment for color sensitive work requires 500 lux, per DIN. And you should adjust your contrast when designing websites for that exact environment. In fact, your monitor MUST be above 300nits if your room has daylight (per DIN)
Ah, well my contrast adjuster is to turn up the brightness as soon as I need to work with designs or photos, but keep it low for reading and for coding.
“Far too contrasty” is a somewhat baffling comment to me. It’s like, um, “this water is far too wet” or something.
Contrast is good for me. It helps my eyes. I am tired of, and by, trendy websites with muted pastels. Give me black on white or white on black, please.
I was genuinely in pain from this amount of contrast. Especially black on white is the worst kind of contrast. As I need accurate colors for work, I’ve got a very bright, high contrast OLED monitor, on which this amount of contrast is genuinely painful to look at (and should only be used for images to detail the contrast between the bright sun and dark shadows, not for text or graphics).
You can always increase contrast easily. But once highlights and shadows have been clipped, you can’t recover them. A LUT can easily map the range #222 to #eee to the range #000 to #fff, compressing all colors between #000 and #222 into #000. But you can’t do the reverse, once you’ve got #000, you don’t know what the original color was. You can easily change your monitor settings to artificially increase contrast or create an ICC profile. But I can’t do the same for recovering the lost dynamic range.
I wonder how many people have such problems?
Myself, I use tools like Reader view to make pages more contrasty, to remove background colours and fancy fonts, and to turn it into black on white.
I wonder if this is adaptable for you, but with some settings to do the reverse?