Tuesday, November 20, 2018
Cognitive Bias Poster
The human brain does an amazingly good job at simplifying the real world into a mental model it can handle. I made a poster of some of the tricks it uses.
Thursday, July 05, 2018
How to do pair programming badly
I tried to do "pair programming" as a teacher/student relationship and it didn't work very well. It was better than nothing but it was extremely stressful for the "student". I suspect that trying it again would quickly lead to burnout.
How to do pair programming right: http://wiki.c2.com/?PairPromiscuously
How to do pair programming right: http://wiki.c2.com/?PairPromiscuously
Why it should be OK to watch youtube at work sometimes (not all day)
https://medium.com/@nilssalzgeber/why-working-in-sprints-maximizes-human-productivity-e8f2eba3d98b
Friday, June 22, 2018
Devlogs are good
I started writing a developer log several years ago when our task tracking functionality was not up to scratch and I was working on an extended project. I wanted to be able to prove that I was working hard, even though I initially had nothing to show for it. However, devlogs actually have several major advantages that make it worthwhile:
1. How did I fix that last time?
Every time I get an error, I copy/paste it into the devlog. Later, I can then search and deal with the same error much more quickly (or at least rule out stuff I tried last time). This makes it easy to answer others' questions when they say, "hey, have you seen this error before" -- I can definitively say "yes, here's what I did" or "no, never seen that".
Every time I fix a problem by googling it, copy/paste the helpful website link, with a few keywords about the problem. This makes it much easier to find the fix if it comes up again.
2. Where was I?
When I get interrupted, it is easy to resume what I was up to, because my mental stack is no longer in my head -- it is written down. If it is not fully up to date, it is at least a hint about what I was doing before I was interrupted. I have found this extremely valuable because getting interrupted while in a high-mental-flow state is no longer a terrible frustration: Getting back into the state now requires a few seconds' reading over my notes instead of (potentially hours) going over old decisions to rebuild the state from scratch.
The devlog has actually become my anchor. Whenever I finish something or resume after an interruption, I scroll back up a little or look at the list of todos maintained at the bottom of the file. It is very comforting knowing that it is always there.
3. Stress management
It is well known by psychologists that identifing feelings by name makes them less intense. I find that documenting my work makes it less stressful, which has become my primary method of dealing with stress. Previously, the only way I could deal with it was by deciding that I didn't care if the project failed, as the concept of caring and having it fail was too much to deal with. Writing down my todos, thoughts, problems, and swearing about them a little in text that others won't read without my permission has been very positive for my mental state and mental health.
4. I don't want to worry about that right now
My time management is better. Apart from being able to easily resume flow after interruptions, I also manage subtasks better. Whenever I have a problem that I can't solve immediately, I describe it and add the word "todo" somewhere. This triggers me to think, "can I fix this in five minutes?". If so, I fix it immediately, and then resume flow by reading over what I was doing beforehand, and so hardly lose any time. If I start it and then it takes more than five minutes, I just abandon it and leave the TODO. If it is important I will add it to a real to-do list somewhere (perhaps at the bottom of the file) so it doesn't get lost. Sometimes I search for the word "todo" and then change the ones I have done to "done" (which is satisfying) and make sure the others are still not urgent, either deleting the word or moving them to a better-tracked list.
The end result of this is that I no longer come back to an old problem and say "oh yeah, I meant to fix that but I forgot.". I now forget intentionally, and don't forget the important things.
5. What did I do this month?
I have a record of what I did. This lets me tell other people what I have been doing -- we got audited last year for research tax breaks and I could provide evidence of the research I was doing. This saved everyone lots of effort in trying to reconstruct how much of my time had been spent on research.
6. Why did I do that again?
When working on an extended project, there is a complete record of all the design problems I considered and all the sources and external resources I investigated as part of the design process. This can be extremely valuable later on, either for onboarding new developers or handing over the project to someone else.
This is partly so important because many projects do not have great test suites. A thorough test suite, with each test's reason for existence well explained, is also very useful. Tests that you don't know why they are there can't be modified. If you understand the requirements, you know what you need to redesign when they change.
7. When did I first think about that?
This approach is standard practise for research scientists (which I used to be, and is where I learned it).
If there is ever a patent dispute or argument about where something came from, I have some evidence about where my ideas came from.
Further reading: https://simpleprogrammer.com/bullet-journal-productivity-programmers/
Friday, May 18, 2018
PostMortem: Why the GIT/Conan toolchain migration took a year instead of the original estimate of a week
1. There was actually quite a lot of development to do. During the course of this project, Jonathan:
* learned Kotlin and TeamCity's DSL (through several iterations)
* learned conan and fixed some issues with it (it evolved from version 0.26 to 1.3)
* wrote many lines of code, generated many more. Often several hundred lines per day for months.
* ran hundreds of test builds in teamcity, and debugged each one
* debugged through many problems with Git, GitVersion, and Git-Tfs
* documented everything
2. We kept changing the design. Each time it changed, there was a cumulative amount of other things to fix. icu, build.py adjustments, package.py adjustments (still going on), is2c changes, re-migrating projects, etc.
* Keeping the list of projects as a static list (is2c/proj_names.py) was a really good decision, as it was very quick to make changes and basically did not require testing
* This was partly unavoidable (we are designing something new, after all) but there were several possible mitigations here.
* Adding people to the project before it was ready introduced more dependencies before the foundations had solidified. This caused a lot of unnecessary work (maybe a cumulative total of three months).
* Trying to migrate all the ti dependencies was a bit of an effort. There were actually lots of dependencies (51, currently) with a very complicated dependency tree, 9 levels deep. This is pretty wild for c++ libraries; most open source projects do not seem to handle this well. The only reason we got away with it until now was the monolithic architecture; unfortunately we are now deeply embedded in the obvious maintainability nightmare that results from that approach. It would have been better to pick a project with only a few dependencies (say, three or four) to do first, and once that was solid, then automatically migrate many more. Unfortunately, Jonathan worked on the REDACTED team so it was politically justifiable to aim for REDACTED projects initially.
* It didn't help that conan was evolving with us. In some ways that was good (they were very responsive to pull requests, often accepting and releasing within 24 hours); but it would have been better to let others evolve conan and pick up a more mature product once it had stabilized. Conan still does not have support for modifying multiple dependencies at once.
3. There were lots of existing problems that needed to be fixed before dependencies could be migrated.
* The monitoring tool was still on an old build of a base library 14 (this took several days to fix, the architecture of that project was chandlerized)
* The python wrapper of our binary protocol was using a checked-in copy of baselibrary.dll because it was initially deemed too hard to set up properly (this is now good, with a conanfile.py to build the dll and then setup.py to package it into a .wheel)
* The python test library still required 3.4 so I implemented support for this and then removed it when the python wrapper turned out to need Python 3.5+ and we had to fix the python test library instead
* Various other minor issues taking hours to days to deal with
4. Writing documentation was not allowed for in the original estimate. Jonathan hadn't written much user documentation in the past and it is quite laborious.
5. Jonathan had other demands on his time from his team. Getting pulled off repeatedly to work on team support or client work meant that the adjustments introduced by c and s were not immediately dealt with and took significantly longer to fix when they were finally discovered. Additionally, it made merges from TFS to Git significantly more involved as many more commits had been made while the merges weren't happening (this could have been handled a lot better by discovering the git-subtree merge process sooner). An additional complication was that the aim was for a solution that would work for most of the company's 1000+ released c++ projects – keeping things simple and flexible was always an additional consideration.
* learned Kotlin and TeamCity's DSL (through several iterations)
* learned conan and fixed some issues with it (it evolved from version 0.26 to 1.3)
* wrote many lines of code, generated many more. Often several hundred lines per day for months.
* ran hundreds of test builds in teamcity, and debugged each one
* debugged through many problems with Git, GitVersion, and Git-Tfs
* documented everything
2. We kept changing the design. Each time it changed, there was a cumulative amount of other things to fix. icu, build.py adjustments, package.py adjustments (still going on), is2c changes, re-migrating projects, etc.
* Keeping the list of projects as a static list (is2c/proj_names.py) was a really good decision, as it was very quick to make changes and basically did not require testing
* This was partly unavoidable (we are designing something new, after all) but there were several possible mitigations here.
* Adding people to the project before it was ready introduced more dependencies before the foundations had solidified. This caused a lot of unnecessary work (maybe a cumulative total of three months).
* Trying to migrate all the ti dependencies was a bit of an effort. There were actually lots of dependencies (51, currently) with a very complicated dependency tree, 9 levels deep. This is pretty wild for c++ libraries; most open source projects do not seem to handle this well. The only reason we got away with it until now was the monolithic architecture; unfortunately we are now deeply embedded in the obvious maintainability nightmare that results from that approach. It would have been better to pick a project with only a few dependencies (say, three or four) to do first, and once that was solid, then automatically migrate many more. Unfortunately, Jonathan worked on the REDACTED team so it was politically justifiable to aim for REDACTED projects initially.
* It didn't help that conan was evolving with us. In some ways that was good (they were very responsive to pull requests, often accepting and releasing within 24 hours); but it would have been better to let others evolve conan and pick up a more mature product once it had stabilized. Conan still does not have support for modifying multiple dependencies at once.
3. There were lots of existing problems that needed to be fixed before dependencies could be migrated.
* The monitoring tool was still on an old build of a base library 14 (this took several days to fix, the architecture of that project was chandlerized)
* The python wrapper of our binary protocol was using a checked-in copy of baselibrary.dll because it was initially deemed too hard to set up properly (this is now good, with a conanfile.py to build the dll and then setup.py to package it into a .wheel)
* The python test library still required 3.4 so I implemented support for this and then removed it when the python wrapper turned out to need Python 3.5+ and we had to fix the python test library instead
* Various other minor issues taking hours to days to deal with
4. Writing documentation was not allowed for in the original estimate. Jonathan hadn't written much user documentation in the past and it is quite laborious.
5. Jonathan had other demands on his time from his team. Getting pulled off repeatedly to work on team support or client work meant that the adjustments introduced by c and s were not immediately dealt with and took significantly longer to fix when they were finally discovered. Additionally, it made merges from TFS to Git significantly more involved as many more commits had been made while the merges weren't happening (this could have been handled a lot better by discovering the git-subtree merge process sooner). An additional complication was that the aim was for a solution that would work for most of the company's 1000+ released c++ projects – keeping things simple and flexible was always an additional consideration.
Saturday, April 07, 2018
Normal conversation
I had a close friend tell me that I am very awkward in conversation. They tend to die.
Here is a short guide about how to do it better:
http://www.improveyoursocialskills.com/conversation/conversation-flow
I'm not sure how I've gotten away with not having internalized all this stuff for so long.
Here is a short guide about how to do it better:
http://www.improveyoursocialskills.com/conversation/conversation-flow
I'm not sure how I've gotten away with not having internalized all this stuff for so long.
Addendum
I later got some very good advice about this -- focus on the situation. A useful technique for getting over the anxiety of a stilted conversation is to express an opinion of something nearby ("I hate that building, it's oppressive" or "these chairs are comfortable, I like them") or something that is happening. Getting over that first-sentence hump was a big deal for me -- I would get anxious about not talking, and the feeling of almost panic made it even harder to think of something to say. Focusing on the local environment or current situation and picking something out to discuss helped a lot.
Sharing an opinion also effectively invites the other person to share their own opinion.
Wednesday, April 04, 2018
The anti-jerk reading list
Start with "Nonviolent communication" , then read "How to talk so kids will listen and listen so kids will talk", and finish with "the seven principles for making marriage work". If you want to go all out, also read "Emotional intelligence".
http://www.improveyoursocialskills.com/conversation is short and good, although it skips the fundamentals above.
Related, although it will only make you seem like less of a jerk: "How to Win Friends and Influence People".
http://www.improveyoursocialskills.com/conversation is short and good, although it skips the fundamentals above.
Related, although it will only make you seem like less of a jerk: "How to Win Friends and Influence People".
Wednesday, March 28, 2018
Datetimes are hard
This is a pretty epic bug discussion: python datetime.timestamp doesn't support leap seconds
related https://zachholman.com/talk/utc-is-enough-for-everyone-right
related https://zachholman.com/talk/utc-is-enough-for-everyone-right
Tuesday, January 02, 2018
Copenhagen is confusing
The word "collapse" is confusing, it is better not to use it.
Just say: the measurement apparatus becomes part of the superposition.
Just say: the measurement apparatus becomes part of the superposition.
Git merge moved folders
Short version: make a patch, change paths, apply patch, resolve merge conflicts, done.
Commands:
Commands:
# git checkout master
# git pull
# git checkout mastertfs
# git pull
# git tfs pull
# git format-patch `git merge-base mastertfs master` --stdout > ../latest.patch
# script-to-change-paths--see-below ../latest.patch
# git checkout master
# git merge -s ours mastertfs --no-commit
# git apply ../latest.patch --ignore-space-change --ignore-whitespace --3way --whitespace=fix
# git commit -m "merge from tfs"
# git push origin master mastertfs
sed --in-place --expression 's|Modules/19.0/main/AAA/|Modules/AAA/src/|g' $1
sed --in-place --expression 's|Modules/19.0/main/BBB/|Modules/BBB/src/|g' $1
sed --in-place --expression 's|Modules/19.0/main/CCC/|Modules/CCC/src/|g' $1
Friday, December 22, 2017
Learn to code
Where do I write code? codechef.com/ide (or, install a language on your computer). If you don't know which language to start with, choose Python 3.5+ (or whatever they're up to now).
What should I write? If you like maths problems, go for https://projecteuler.net/problem=1. If you like secrets, try the matasano challenge. If you would like to make games, try gamefroot.com.
What should I write? If you like maths problems, go for https://projecteuler.net/problem=1. If you like secrets, try the matasano challenge. If you would like to make games, try gamefroot.com.
Thursday, August 03, 2017
StrCmpLogicalW
I present, a sorted list:
--
-
-
.
_
1
2
a
b
.-
-.
.
..
._
.1
.2
.a
.b
_-
-_
_
_.
__
_1
_2
_a
_b
1-
-1
1
1.
1_
1a
1b
2-
-2
2
2.
2_
2a
2b
11
12
21
22
a-
-a
a
a.
a_
a1
a2
aa
ab
b-
-b
b
b.
b_
b1
b2
ba
bb
Sorted according to what order, you might say? This is how Windows sorts filenames.
--
-
-
.
_
1
2
a
b
.-
-.
.
..
._
.1
.2
.a
.b
_-
-_
_
_.
__
_1
_2
_a
_b
1-
-1
1
1.
1_
1a
1b
2-
-2
2
2.
2_
2a
2b
11
12
21
22
a-
-a
a
a.
a_
a1
a2
aa
ab
b-
-b
b
b.
b_
b1
b2
ba
bb
Sorted according to what order, you might say? This is how Windows sorts filenames.
Tuesday, March 14, 2017
The Story of the Iceberg Tests
The Story of the Iceberg Tests
About two years after I started at a financial software company, I was assigned to fix a problem with iceberg orders for a Canadian market. Our Trading Interface was not sending the correct display volume to the order server.
I was starting to get sick of these jobs because we had already had several problems with icebergs. Every market represents them differently -- one will tell you just the visible volume, another the hidden volume, and another might only tell you the refill quantity when the order is created. Since it is our job to normalize all this for the order server, we have to handle all the variations and produce a complete picture for the order server.
What was happening was that every time someone would fix icebergs for one market, it would break for another. Annoyed by this, I spent two days combing through all the markets' code, gathered all the implicit requirements, and after verifying a few other requirements with knowledgeable people, I wrote a complete set of unit tests. I was then able, through a fair bit of trial and error, to produce normalization code that worked for all the markets. In total, it took nearly a week of careful, painstaking work, and resulted in 26 unique tests and all the TIs were able to handle their markets' idiosyncracies at the same time.
The problems with icebergs vanished overnight.
About a year later, my boss decided our normalization code needed to be refactored. He discussed it with us, and I said to go for it, but to make sure to pass all the tests. He was a cowboy coder -- his test strategy was to manually test the happy path and let clients find the other problems.
(This approach was really awful: it took us days to find the cause of a problem, pulling logs and stepping through code. There were also lots of problems, two or three every week. It also meant that clients became resistant to upgrading. The end result was that our team spent more than half its time on support.)
My boss was unable to write code that passed all the tests. He gave up after several weeks. I felt victorious -- without those tests, it is likely that I would have spent several months in a painful release-investigate-debug cycle, fixing the same iceberg problems over and over again.
Fortunately for software engineers, building the bridge between the user and the machine is not like civil engineering. Our builds take seconds, not years. It is easy for us to build the bridge a hundred times a day, reinforcing parts of the code that are under tension or stress. As a result, it should be easy for us to find problems with our design.
Just like with a real bridge, reinforcing the more fragile parts of your code with full-coverage tests can save huge amounts of pain in missed deadlines and exhausted engineers.
Here is how to write good tests (as taught in every undergraduate software engineering course):
- Cover both branches of every branch. Make sure your tests fully explore all possible outcomes of the code. Only someone working with the source code can do this.
- Cover every requirement.
- Test each equivalence class once and only once. An equivalence class is all inputs that result in the same calculation process but with slightly different numbers. So an order with quantity=7 and quantity=8 would be in the same equivalence class.
- Test each edge case once and only once. See what happens when we send quantity=0 or quantity=-1 or quantity=blank.
- Test anything else that seems likely to break in the future
I have repeated this process twice, for multileg cancellations and the IosAttributeFilter. It worked every time.
Even better, the tests are still saving us work. A team member came to ask me about a failing iceberg test the other day, so I pointed out a market that would have broken if they made the change they wanted to make. They found a better solution to their market's problem.
previously: unit testing: writing better code faster
Addendum
I have grown as a programmer since I wrote this article. I would recommend reading the following:http://blog.stevensanderson.com/2009/08/24/writing-great-unit-tests-best-and-worst-practises/
(particularly the point about the two sweet spots of testing, highly-focused ones for validating a complicated component vs. integration tests that are cheap to maintain)
Good tests are useful as documentation as well. I don't have to support old TIs I wrote because I can just refer the new developers (or my own failed memory) to the tests when they have a question about how or why it is coded that way, and whether they can change something. How is answered by debugging through the code (made easy by the test providing typical input and a harness); Why is answered by the test's description and existence. Whether they can change something is answered by "change it and see if a test breaks" which is a damn sight better than the previous "we're too afraid to change anything because we have no way of knowing if we've broken something without giving it to clients". It is important that the tests are written with these things in mind; writing a test that will not be useful when it fails is counterproductive because it makes the test suite overwhelming and thus not useful as documentation. So "100% coverage" is not a good thing to aim for with the above goals in mind. Test the hard stuff; test the API boundary; but don't test the wiring / glue code between modules.
Wednesday, February 15, 2017
Distinct colours #2
I have updated the category-colors tool to use the CIECAM02-UCS color space, because the spacing is much better than d3's default CIELAB. (previously)
Here is the tool: http://jnnnnn.github.io/category-colors-constrained.html
Here are some results.
Good Colors Against A White Background
function constraint(jab) {
return jab_dist(d3.jab("white"), jab) > 35
&& jab.a**2 + jab.b**2 > 10**2
&& jab.J > 50;
}
"#1b70fc", "#faff16", "#d50527", "#158940", "#f898fd", "#24c9d7", "#cb9b64", "#866888", "#22e67a", "#e509ae", "#9dabfa", "#437e8a", "#b21bff", "#ff7b91", "#94aa05", "#ac5906", "#82a68d", "#fe6616", "#7a7352", "#f9bc0f", "#b65d66", "#07a2e6", "#c091ae", "#8a91a7", "#88fc07", "#ea42fe", "#9e8010", "#10b437", "#c281fe", "#f92b75", "#07c99d", "#a946aa", "#bfd544", "#16977e", "#ff6ac8", "#a88178", "#5776a9", "#678007", "#fa9316", "#85c070", "#6aa2a9", "#989e5d", "#fe9169", "#cd714a", "#6ed014", "#c5639c", "#c23271", "#698ffc", "#678275", "#c5a121", "#a978ba", "#ee534e", "#d24506", "#59c3fa", "#ca7b0a", "#6f7385", "#9a634a", "#48aa6f", "#ad9ad0", "#d7908c", "#6a8a53", "#8c46fc", "#8f5ab8", "#fd1105", "#7ea7cf", "#d77cd1", "#a9804b", "#0688b4", "#6a9f3e", "#ee8fba", "#a67389", "#9e8cfe", "#bd443c", "#6d63ff", "#d110d5", "#798cc3", "#df5f83", "#b1b853", "#bb59d8", "#1d960c", "#867ba8", "#18acc9", "#25b3a7", "#f3db1d", "#938c6d", "#936a24", "#a964fb", "#92e460", "#a05787", "#9c87a0", "#20c773", "#8b696d", "#78762d", "#e154c6", "#40835f", "#d73656", "#1afd5c", "#c4f546", "#3d88d8", "#bd3896", "#1397a3", "#f940a5", "#66aeff", "#d097e7", "#fe6ef9", "#d86507", "#8b900a", "#d47270", "#e8ac48", "#cf7c97", "#cebb11", "#718a90", "#e78139", "#ff7463", "#bea1fd"
A wide range of colors
jab.J > 45 && jab.J < 95:
"#3957ff", "#d3fe14", "#c9080a", "#fec7f8", "#0b7b3e", "#0bf0e9", "#c203c8", "#fd9b39", "#888593", "#906407", "#98ba7f", "#fe6794", "#10b0ff", "#ac7bff", "#fee7c0", "#964c63", "#1da49c", "#0ad811", "#bbd9fd", "#fe6cfe", "#297192", "#d1a09c", "#78579e", "#81ffad", "#739400", "#ca6949", "#d9bf01", "#646a58", "#d5097e", "#bb73a9", "#ccf6e9", "#9cb4b6", "#b6a7d4", "#9e8c62", "#6e83c8", "#01af64", "#a71afd", "#cfe589", "#d4ccd1", "#fd4109", "#bf8f0e", "#2f786e", "#4ed1a5", "#d8bb7d", "#a54509", "#6a9276", "#a4777a", "#fc12c9", "#606f15", "#3cc4d9", "#f31c4e", "#73616f", "#f097c6", "#fc8772", "#92a6fe", "#875b44", "#699ab3", "#94bc19", "#7d5bf0", "#d24dfe", "#c85b74", "#68ff57", "#b62347", "#994b91", "#646b8c", "#977ab4", "#d694fd", "#c4d5b5", "#fdc4bd", "#1cae05", "#7bd972", "#e9700a", "#d08f5d", "#8bb9e1", "#fde945", "#a29d98", "#1682fb", "#9ad9e0", "#d6cafe", "#8d8328", "#b091a7", "#647579", "#1f8d11", "#e7eafd", "#b9660b", "#a4a644", "#fec24c", "#b1168c", "#188cc1", "#7ab297", "#4468ae", "#c949a6", "#d48295", "#eb6dc2", "#d5b0cb", "#ff9ffb", "#fdb082", "#af4d44", "#a759c4", "#a9e03a", "#0d906b", "#9ee3bd", "#5b8846", "#0d8995", "#f25c58", "#70ae4f", "#847f74", "#9094bb", "#ffe2f1", "#a67149", "#936c8e", "#d04907", "#c3b8a6", "#cef8c4", "#7a9293", "#fda2ab", "#2ef6c5", "#807242", "#cb94cc", "#b6bdd0", "#b5c75d", "#fde189", "#b7ff80", "#fa2d8e", "#839a5f", "#28c2b5", "#e5e9e1", "#bc79d8", "#7ed8fe", "#9f20c3", "#4f7a5b", "#f511fd", "#09c959", "#bcd0ce", "#8685fd", "#98fcff", "#afbff9", "#6d69b4", "#5f99fd", "#aaa87e", "#b59dfb", "#5d809d", "#d9a742", "#ac5c86", "#9468d5", "#a4a2b2", "#b1376e", "#d43f3d", "#05a9d1", "#c38375", "#24b58e", "#6eabaf", "#66bf7f", "#92cbbb", "#ddb1ee", "#1be895", "#c7ecf9", "#a6baa6", "#8045cd", "#5f70f1", "#a9d796", "#ce62cb", "#0e954d", "#a97d2f", "#fcb8d3", "#9bfee3", "#4e8d84", "#fc6d3f", "#7b9fd4", "#8c6165", "#72805e", "#d53762", "#f00a1b", "#de5c97", "#8ea28b", "#fccd95", "#ba9c57", "#b79a82", "#7c5a82", "#7d7ca4", "#958ad6", "#cd8126", "#bdb0b7", "#10e0f8", "#dccc69", "#d6de0f", "#616d3d", "#985a25", "#30c7fd", "#0aeb65", "#e3cdb4", "#bd1bee", "#ad665d", "#d77070", "#8ea5b8", "#5b5ad0", "#76655e", "#598100", "#86757e", "#5ea068", "#a590b8", "#c1a707", "#85c0cd", "#e2cde9", "#dcd79c", "#d8a882", "#b256f9", "#b13323", "#519b3b", "#dd80de", "#f1884b", "#74b2fe", "#a0acd2", "#d199b0", "#f68392", "#8ccaa0", "#64d6cb", "#e0f86a", "#42707a", "#75671b", "#796e87", "#6d8075", "#9b8a8d", "#f04c71", "#61bd29", "#bcc18f", "#fecd0f", "#1e7ac9", "#927261", "#dc27cf", "#979605", "#ec9c88", "#8c48a3", "#676769", "#546e64", "#8f63a2", "#b35b2d", "#7b8ca2", "#b87188", "#4a9bda", "#eb7dab", "#f6a602", "#cab3fe", "#ddb8bb", "#107959", "#885973", "#5e858e", "#b15bad", "#e107a7", "#2f9dad", "#4b9e83", "#b992dc", "#6bb0cb", "#bdb363", "#ccd6e4", "#a3ee94", "#9ef718", "#fbe1d9", "#a428a5", "#93514c", "#487434", "#e8f1b6", "#d00938", "#fb50e1", "#fa85e1", "#7cd40a", "#f1ade1", "#b1485d", "#7f76d6", "#d186b3", "#90c25e", "#b8c813", "#a8c9de", "#7d30fe", "#815f2d", "#737f3b", "#c84486", "#946cfe", "#e55432", "#a88674", "#c17a47", "#b98b91", "#fc4bb3", "#da7f5f", "#df920b", "#b7bbba", "#99e6d9", "#a36170", "#c742d8", "#947f9d", "#a37d93", "#889072", "#9b924c", "#23b4bc", "#e6a25f", "#86df9c", "#a7da6c", "#3fee03", "#eec9d8", "#aafdcb", "#7b9139", "#92979c", "#72788a", "#994cff", "#c85956", "#7baa1a", "#de72fe", "#c7bad8", "#85ebfe", "#6e6089", "#9b4d31", "#297a1d", "#9052c0", "#5c75a5", "#698eba", "#d46222", "#6da095", "#b483bb", "#04d183", "#9bcdfe", "#2ffe8c", "#9d4279", "#c909aa", "#826cae", "#77787c", "#a96fb7", "#858f87", "#fd3b40", "#7fab7b", "#9e9edd", "#bba3be", "#f8b96c", "#7be553", "#c0e1ce", "#516e88", "#be0e5f", "#757c09", "#4b8d5f", "#38b448", "#df8780", "#ebb3a0", "#ced759", "#f0ed7c", "#e0eef1", "#0969d2", "#756446", "#488ea8", "#888450", "#61979c", "#a37ad6", "#b48a54", "#8193e5", "#dd6d89", "#8aa29d", "#c679fe", "#a4ac12", "#75bbb3", "#6ae2c1", "#c4fda7", "#606877", "#b2409d", "#5874c7", "#bf492c", "#4b88cd", "#e14ec0", "#b39da2", "#fb8300", "#d1b845", "#c2d083", "#c3caef", "#967500", "#c56399", "#ed5a05", "#aadff6", "#6685f4", "#1da16f", "#f28bff", "#c9c9bf", "#c7e2a9", "#5bfce4", "#e0e0bf", "#e8e2e8", "#ddf2d8", "#9108f8", "#932dd2", "#c03500", "#aa3fbc", "#547c79", "#9f6045", "#04897b", "#966f32", "#d83212", "#039f27", "#df4280", "#ef206e", "#0095f7", "#a5890d", "#9a8f7f", "#bc839e", "#88a23b", "#e55aed", "#51af9e", "#5eaf82", "#9e91fa", "#f76c79", "#99a869", "#d2957d", "#a2aca6", "#e3959e", "#adaefc", "#5bd14e", "#df9ceb", "#fe8fb1", "#87ca80", "#fc986d", "#2ad3d9", "#e8a8bb", "#a7c79c", "#a5c7cc", "#7befb7", "#b7e2e0", "#85f57b", "#f5d95b", "#dbdbff", "#fddcff", "#6e56bb", "#226fa8", "#5b659c", "#58a10f", "#e46c52", "#62abe2", "#c4aa77", "#b60e74", "#087983", "#a95703", "#2a6efb", "#427d92",
Light colors
jab.J > 80:
"#96caff", "#faff16", "#fda899", "#13fcbb", "#f7fef7", "#eba6ff", "#bcca83", "#57e502", "#feaf23", "#c6c0cb", "#38f1fd", "#9ccfbe", "#f9feae", "#a6fe8e", "#bfcd03", "#ebd3b6", "#fec0df", "#c2eefc", "#cac2fc", "#bcfcd6", "#f4e5ff", "#f9dc56", "#80da93", "#ebb678", "#b1fb27", "#30dfc8", "#8dd0df", "#1ff479", "#cedbd8", "#d2e6b8", "#85feea", "#97db5c", "#deb7b7", "#cad5ef", "#d0eb6d", "#fe9fe1", "#bdc7ad", "#fcdadf", "#d9bae2", "#dac05a", "#ecda91", "#fef5d0", "#ffa4bc", "#43d9fd", "#d2fcf5", "#b1c7d0", "#e6f0fb", "#83e5bd", "#b2e598", "#fdfb73", "#73fd58", "#a6cfa2", "#a1e3e1", "#e4e21c", "#8ef8b0", "#e2c30e", "#fdc7ff", "#cefeb9", "#0be597", "#d4c491", "#fdc5b5", "#e4fdde", "#d5ccc7", "#acdaf8", "#e4c9da", "#fdc664", "#badfc5", "#bbd15f", "#fdf1f7", "#b4c3ed", "#93e8fd", "#ddd9e3", "#e5e5d7", "#98e116", "#e0d0fb", "#fccd95", "#aafdfe", "#5cd9de", "#deba9d", "#dce994", "#d6b2fb", "#c5bedd", "#bec4c3", "#feab79", "#82ea7f", "#fed7f3", "#8affd2", "#d6fc50", "#e8aed9", "#f7b7c2", "#2bf4e5", "#ffad53", "#11ff13", "#d3fe94", "#c2e530", "#aef161", "#fee5d4", "#a3d378", "#dad351", "#fec328", "#a4e3b3", "#a9efd9", "#feeb8a", "#85d4cd", "#e0b4c9", "#55e358", "#bddbe1", "#48ffa0", "#d6eee1", "#fee3b0", "#ff99fd", "#3fe0b0", "#84d7fe", "#7be9d3", "#1bf3cd", "#d4d4af", "#d6d278", "#70fffe", "#aac8e1", "#ffb0fe", "#b5ccbf", "#c6ccd5", "#ccd9c5", "#feee3e", "#cdc0ab", "#58e182", "#e9b8f8", "#f7bc97", "#b4e275", "#bee8e1", "#e1e3ff", "#ebebb2", "#8fd4af", "#c0d3a2", "#e7c67d", "#6ceba9", "#d8cde4", "#eacdc6", "#d4eef1", "#e7e8e9", "#7deaec", "#88f31d", "#c0efbe", "#75d8be", "#a0d1d3", "#fdacd4", "#09f64f", "#c4dc8c", "#d0d1fc", "#ecccf1", "#f7d517", "#d0dde8", "#e7dadc", "#78fe8b", "#e6e373", "#cde5fd", "#b1fead", "#e7fec5", "#e9fdfd", "#b9c3d9", "#cebec1", "#eab3a4", "#edb84a", "#8aea52", "#bad5fd", "#a7ddea", "#f9c9d5", "#f2deec", "#fbf4e7", "#b3d8cf", "#feb9ed", "#96e89d", "#d5d99c", "#efd16f", "#ecfb92", "#d0bbd2", "#89d97b", "#cdc47c", "#acd345", "#21e0f4", "#9ed693", "#9adfcd", "#ccdc4c", "#e3e3c2", "#d8f003", "#e7f158", "#b8fff2", "#cdb9e9", "#faa8ae", "#cac75c", "#d7d119", "#eccb48", "#bbd7ea", "#a0f3c1", "#cbeaa6", "#c5f4e0", "#ede9f5", "#b8bffd", "#97cced", "#70dba6", "#72dbf2", "#e6bddd", "#ddc2fe", "#b8ddb0", "#e7d29e", "#c7ec8c", "#d1edce", "#c8fb75", "#f9ffe8", "#e9abeb", "#b5d1b3", "#feb6af", "#ffba6e", "#92f772", "#e8f4cb", "#ffeda8", "#e5ff77", "#f6f8fe", "#c4c699", "#c5ca3f", "#e9bb29", "#19e2dc", "#d9bfb4", "#c6cabf", "#e6c091", "#bcd0d0", "#d5cbd3", "#aae346", "#d0d2d3", "#f1c7ab", "#72ef95", "#cdda6f", "#d9d5c0", "#a2eb81", "#72f3cc", "#91efe6", "#fed284", "#a6edf6", "#4bfee0", "#9bfb4f", "#c1f3f6", "#e0f9ab", "#d4ffd2", "#fffac1", "#a9c5fd", "#f0acc4", "#7adf34", "#a8d516", "#86dade", "#6cdfd7", "#a7d5b9", "#b4d899", "#f1c0bf", "#a9e8c7", "#e6e04c", "#fcd4bf", "#67feb6", "#ddfeee", "#f5af92", "#a7cddc", "#c7cae9", "#e1c2c9", "#06f6a4", "#64f36c", "#ffd056", "#e9d3ea", "#94f595", "#eadbcc", "#acf2b2", "#a2fde0", "#f5e5c6", "#f7eb67", "#f1e9e6", "#e2efdb", "#fbfd52", "#aacac8", "#6fdf6e", "#23e83d", "#d6c33e", "#edbb66", "#96d8f1", "#d9c9a7", "#30ef8c", "#66ed41", "#eabeed", "#c3dbce", "#d6d6e9", "#f8c6ee", "#d5d9d1", "#96f0cc", "#c7ef56", "#8ef6ff", "#aff0e8", "#b7f593", "#dee7e0", "#bdff5c", "#eff01a", "#d7f0fe", "#bdfdc3", "#f0ef9c", "#d3fe14", "#bec0ea", "#c6c2be", "#eab1b3",
White background colors
function constraint(jab) {
let d = jab_dist(d3.jab("white"), jab);
return d > 48 && d < 60;
}
"#525c72", "#21ce03", "#fe3302", "#fa46ff", "#876d0d", "#a24568", "#159bfc", "#14926d", "#9149f8", "#fd5a93", "#85706a", "#ad4d1e", "#4a8b00", "#a96bb1", "#04859b", "#4d6643", "#e2770f", "#4567c6", "#d517a9", "#76527b", "#c310f3", "#d5324b", "#7c78a9", "#657b79", "#7d80fd", "#949401", "#b46965", "#017065", "#6f864d", "#805432", "#12ad4b", "#b86dfd", "#b0743b", "#c66495", "#eb6555", "#5c5c59", "#0e7b3a", "#7b5057", "#5785b7", "#a14297", "#986782", "#884eaf", "#ff3fc6", "#296b97", "#5e700c", "#e6147f", "#635b98", "#746b7b", "#6fa914", "#756c44", "#af830a", "#d85311", "#d25ac2", "#994d45", "#62768b", "#476263", "#7369ce", "#ff3d60", "#985b0b", "#2e7cfe", "#13938d", "#966ec9", "#d85d73", "#537f60", "#45934d", "#fe6001", "#166d7a", "#886996", "#c55345", "#cd5aeb", "#db07dc", "#9a674c", "#c1387f", "#719433", "#cd6c42", "#666f60", "#586d9d", "#067bca", "#bb6005", "#675665", "#aa4cbe", "#2b6f4d", "#5e66fb", "#a341e1", "#927941", "#1295ca", "#996efb", "#675942", "#b24250", "#dd3a2a", "#894b70", "#7c8011", "#407d7e", "#946064", "#df4aa2", "#7182cd", "#635879", "#60642a", "#4e763b", "#21b10b", "#a85587", "#b65c74", "#7a5d20", "#606971", "