Skip to content

Instantly share code, notes, and snippets.

@juliendehos
Last active July 11, 2025 09:12
Show Gist options
  • Save juliendehos/31b6a74eba60d95209a58deaf5440984 to your computer and use it in GitHub Desktop.
Save juliendehos/31b6a74eba60d95209a58deaf5440984 to your computer and use it in GitHub Desktop.
on handling colors in three.hs
// in three.js, there are various ways to define a color:
// using an int
const light1 = new THREE.AmbientLight(0xff0000);
// using a predefined color (string)
const light2 = new THREE.AmbientLight("green");
// using a rgb triplet
const light3 = new THREE.AmbientLight(new THREE.Color(0,0,1));
// using an existing color
const light4 = new THREE.AmbientLight(new THREE.Color(0xffff00));
const light5 = new THREE.AmbientLight(new THREE.Color("cyan"));
const light6 = new THREE.AmbientLight(new THREE.Color(new THREE.Color(1,0,1)));
-- we can do that in three.hs too, using typeclasses (like we do with Triplet for 3D vectors)
-- a typeclass for creating a Color using an int, a predefined color, a rgb triplet or an existing color
class RgbClass rgb where
toRgbVal :: rgb -> JSM JSVal
-- ints
instance RgbClass Int where
toRgbVal = toJSVal
-- predefined colors
data ColorKeyword
= Cyan
| Green
instance RgbClass ColorKeyword where
toRgbVal k = toJSVal $ case k of
Cyan -> "cyan" :: JSString
Green -> "green"
-- rgb triplets
-- we could also use (Double, Double, Double) but this requires FlexibleInstances and some type annotations
data Rgb = Rgb Double Double Double
instance RgbClass Rgb where
toRgbVal (Rgb r g b) = toJSVal (r, g, b)
-- existing colors
instance RgbClass Color where
toRgbVal = pure . unColor
-- a function for creating a color
newColor :: RgbClass rgb => rgb -> THREE.Three Color
newColor rgb' = THREE.new Color "Color" (toRgbVal rgb')
-- some three.js classes take a color parameter as an int, a string or an existing color, but not a rgb triplet
-- so we can define another typeclass:
class ColorClass color where
toColorVal :: color -> JSM JSVal
-- ints
instance ColorClass Int where
toColorVal = toRgbVal
-- predefined colors
instance ColorClass ColorKeyword where
toColorVal = toRgbVal
-- existing colors
instance ColorClass Color where
toColorVal = toRgbVal
-- no instance for rgb triplets
-- now we can use ColorClass to define a color argument
newAmbientLight :: ColorClass color => color -> THREE.Three AmbientLight
newAmbientLight color' = THREE.new AmbientLight "AmbientLight" (toColorVal color')
-- this works in the same way as in three.js
light1 <- newAmbientLight (0x110000 :: Int)
light2 <- newAmbientLight Green
light3 <- newAmbientLight =<< THREE.Color.newColor (Rgb 0 0 1)
light4 <- newAmbientLight =<< THREE.Color.newColor (0x111100 :: Int)
light5 <- newAmbientLight =<< THREE.Color.newColor Cyan
light6 <- newAmbientLight =<< THREE.Color.newColor =<< THREE.Color.newColor (Rgb 0 0 1)
-- not allowed:
-- light7 <- newAmbientLight (Rgb 0 0 1)
@juliendehos
Copy link
Author

Three.js also supports strings like:

const color6 = new THREE.Color("hsl(0, 100%, 50%)");

So implementing string colors with something like ColorKeyword isn't enough.
Anyway, the recommended way to define a color is:

const color2 = new THREE.Color( 0xff0000 );

🤷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment