├── .gitignore
├── LICENSE
├── README.md
├── components
└── nav.js
├── out
├── .nojekyll
├── 404.html
├── CNAME
├── IBMPlexMono-Regular.woff
├── IBMPlexMono-Regular.woff2
├── IBMPlexSans-Regular.woff
├── IBMPlexSans-Regular.woff2
├── IBMPlexSerif-Regular.woff
├── IBMPlexSerif-Regular.woff2
├── _next
│ └── static
│ │ ├── PZa48Eu1Xq1KR0uaqScuq
│ │ └── pages
│ │ │ ├── _app.js
│ │ │ ├── _error.js
│ │ │ └── index.js
│ │ ├── chunks
│ │ ├── 0.js
│ │ ├── 0.js.map
│ │ └── commons.ee207d0e464a70a92a54.js
│ │ ├── development
│ │ ├── dll
│ │ │ ├── dll_d6a88dbe3071bd165157.js
│ │ │ └── dll_d6a88dbe3071bd165157.js.map
│ │ └── pages
│ │ │ ├── _app.js
│ │ │ ├── _app.js.map
│ │ │ ├── _error.js
│ │ │ ├── _error.js.map
│ │ │ ├── index.js
│ │ │ └── index.js.map
│ │ ├── nHQpeNZagzw2d5S3_6gA5
│ │ └── pages
│ │ │ ├── _app.js
│ │ │ ├── _error.js
│ │ │ └── index.js
│ │ ├── o3wOKG0jVbuWMvD0znjMG
│ │ └── pages
│ │ │ ├── _app.js
│ │ │ ├── _error.js
│ │ │ └── index.js
│ │ ├── runtime
│ │ ├── amp.js
│ │ ├── amp.js.map
│ │ ├── main-dea3d76b88b8867bf0cc.js
│ │ ├── main.js
│ │ ├── main.js.map
│ │ ├── polyfills-510791b1bc66cc8557b9.js
│ │ ├── polyfills.js
│ │ ├── polyfills.js.map
│ │ ├── webpack-08f7b238829422e3b9b2.js
│ │ ├── webpack.js
│ │ └── webpack.js.map
│ │ └── webpack
│ │ ├── 3ad2843ba247d80b22cd.hot-update.json
│ │ ├── d78c17e5974b1cd73546.hot-update.json
│ │ └── static
│ │ └── development
│ │ └── pages
│ │ ├── index.js.d78c17e5974b1cd73546.hot-update.js
│ │ └── index.js.d78c17e5974b1cd73546.hot-update.js.map
├── christmasst.jpeg
├── diagram.png
├── diagram2.png
├── favicon.ico
├── favicon.png
├── fka.jpg
├── grant.jpg
├── grant.png
├── gray.png
├── index.html
├── lion.jpg
├── matrix.jpg
├── pal.gif
├── pal.png
├── paristx.jpeg
├── scruggs.jpg
├── wick.jpg
└── yyy.jpg
├── package-lock.json
├── package.json
├── pages
├── _document.js
└── index.js
├── public
├── IBMPlexMono-Regular.woff
├── IBMPlexMono-Regular.woff2
├── IBMPlexSans-Regular.woff
├── IBMPlexSans-Regular.woff2
├── IBMPlexSerif-Regular.woff
├── IBMPlexSerif-Regular.woff2
├── christmasst.jpeg
├── diagram.png
├── diagram2.png
├── favicon.ico
├── favicon.png
├── fka.jpg
├── grant.jpg
├── grant.png
├── gray.png
├── lion.jpg
├── matrix.jpg
├── pal.gif
├── pal.png
├── paristx.jpeg
├── scruggs.jpg
├── wick.jpg
└── yyy.jpg
├── s
├── theme_min.js
└── themes.js
├── scripts
└── data_clean.js
└── shaders
└── shaders.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 |
14 | # production
15 | /build
16 |
17 | # misc
18 | .DS_Store
19 | .env*
20 |
21 | # debug
22 | npm-debug.log*
23 | yarn-debug.log*
24 | yarn-error.log*
25 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 constraint systems
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pal
2 |
3 |
6 |
7 | Apply an eight-color terminal palette to an image. Use keyboard controls to choose a theme, set thresholds, and cycle hues.
8 |
9 | https://pal.constraint.systems
10 |
11 | ## How it works
12 |
13 | ### Applying the base colors
14 |
15 | Pal looks at each pixel of the image. It first looks at luminance. If the luminance value is lower than the `lothresh` threshold, the pixel is set to black. If the luminance value is higher than the `hithresh` threshold, the pixel is set to white. If it is between the thresholds, it uses a formula to calculate the hue. The pixel is then set to the closest hue (from red, yellow, green, blue, magenta and cyan). If the color has no hue (if its red, green, and blue values are equal) it is set to black or white based on a luminance threshold of 0.5.
16 |
17 | ### Applying a theme
18 |
19 | To apply a theme Pal uses the palette categories from the base color values: red pixels are set to whatever is in that theme's red slot, etc. An alternative way to do it would be to recalculate the closest hue based on the theme's colors (this is how color palettes are usually applied). But Pal doesn't do that. Pal uses the calculations from the base colors and applies the theme on top of that. For black and white Pal uses the background and foreground colors from the theme. It uses whichever one is darker for the black pixels (so dark themes use the background color for black while light themes use the foreground color).
20 |
21 | `shue` shifts the hue slots according to its value. The background and foreground colors remain the same.
22 |
23 | ### The shader
24 |
25 | I use a [shader](https://github.com/constraint-systems/pal/blob/master/shaders/shaders.js) to apply the themes. Originally I was doing canvas pixel manipulation, but the rendering was taking a long time. Switching to a shader made the theme changes instantaneous. I used [Mike Riethmuller's wonderful guide to get the shader working](https://www.madebymike.com.au/writing/canvas-image-manipulation/), modifying it to fit inside a React app. I'm pretty sure I'm not supposed to use so many if statements in a shader, so if anyone wants to clue me in to a better way to write [the fragment shader](/constraint-systems/pal/blob/master/shaders/shaders.js) I'd be happy to hear it.
26 |
27 | ### Themes
28 |
29 | The themes are from the [Gogh terminal color scheme project](https://github.com/Mayccoll/Gogh). I converted them into a more minimal [JSON file](https://github.com/constraint-systems/pal/blob/master/s/theme_min.js). Let me know if you'd like a theme added and have the color values.
30 |
--------------------------------------------------------------------------------
/components/nav.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 |
4 | const links = [
5 | { href: 'https://zeit.co/now', label: 'ZEIT' },
6 | { href: 'https://github.com/zeit/next.js', label: 'GitHub' },
7 | ].map(link => {
8 | link.key = `nav-link-${link.href}-${link.label}`
9 | return link
10 | })
11 |
12 | const Nav = () => (
13 |
54 | )
55 |
56 | export default Nav
57 |
--------------------------------------------------------------------------------
/out/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/constraint-systems/pal/7cdc1d2006735674f419e2a8fcbbd1ef86f4246e/out/.nojekyll
--------------------------------------------------------------------------------
/out/404.html:
--------------------------------------------------------------------------------
1 |