Skip to content
Swifter edited this page Oct 19, 2024 · 19 revisions

All colors in the beatmap are read as an RGBA (red, green, blue, alpha) array, where each channel's influence is encoded between 0-1.

[r, g, b, a]

  • Note: RGBA values can actually exceed 1, which in terms of lights, causes them to be brighter.

Before the map is saved, ReMapper provides some useful ways to generate the RGB part of colors by utilizing different formats.

Hexidecimal

Hexidecimal numbers store RGB values in a "base 16" format, meaning that we "carry the 1" at 16 instead of 10. You are probably used to seeing it in other programs, it looks something like:

#A9FBE3

Using the rm.HEXtoRGB function, you can input a string like above and get out the RGB equivalent:

rm.HEXtoRGB("#A9FBE3") // ~ [0.66, 0.98, 0.89]
  • Tip: This is particularly useful for using a color picker if you want to hardcode colors into your script.

image

const backLasersColor = rm.HEXtoRGB("#eb4034") // paste in result here
rm.basicEvent.backLasers(map).on(backLasersColor)

The rm.RGBtoHEX function lets you do the reverse, and input an RGB array to get out a HEX string:

rm.HEXtoRGB(0.66, 0.98, 0.89) // ~ #A9FBE3

HSV (Hue, Saturation, Value)

While RGB reads "red", "green", and "blue" channels, HSV reads as quantities of "hue", "saturation", and "value":

  • Hue: The pure color being displayed, it would be like going around a color wheel. Different hues could be "green", "blue", "red", "yellow".. etc.
  • Saturation: The vibrance of the color. Zero saturation is completely grayscale, while full saturation is super punchy, pure and bright colors.
  • Value: The amount of the color, think of it like a multiplier for the brightness of the color. Zero value is black, while full value is the untouched color made by the other two parameters.

image

This format is an extremely intuitive way to quantify color, which is why you see it in so many color pickers.


RGB colors can be generated from HSV values using the rm.HSVtoRGB function:

rm.HSVtoRGB(0, 1, 1) // ~ [1, 0, 0]

Similarly, HSV colors can be generated from RGB values using the rm.RGBtoHSV function:

rm.RGBtoHSV(1, 0, 0) // ~ [0, 1, 1]

Blending Between Colors

If you want to interpolate (blend) colors in the most simple way possible, you can just interpolate all of their values individually. This can be done using the rm.lerpRGB function. (see Lerp)

rm.lerpRGB([1, 0, 0], [0, 1, 1], 0.5) // ~ [0.5, 0.5, 0.5]

You can visualize this as essentially drawing a straight line between our two colors on the color wheel.

image

  • Note: Because of our perception of colors, the line might not be completely straight. Though, it is a useful way to think about it.

However, you can also convert the colors to HSV, interpolate their components (while making sure that the hue is SPHERICALLY interpolated), and then convert them back to HSV. This can be done using the rm.lerpHSV function.

rm.lerpHSV([1, 0, 0], [0, 1, 1], 0.5) // ~ [0.5, 1, 0]

Notice how the resulting color is different now. This time, you can imagine that we're drawing an arc around the color wheel to connect our two colors.

image


To further demonstrate this difference, take a look at what happens if we compare the gradients of the two interpolations side by side. The top half is interpolated with RGB, and the bottom half is interpolated with HSV.

image

In general, interpolating through RGB tends to desaturate the colors as it crosses the color wheel. In contrast, HSV stays within the same saturation as much as possible. As a result, HSV typically tends to give punchier, though more wildly shifting gradients.


If you're curious, this image was generated with this code using the Image class.

Code
const width = 400
const height = 300
const image = rm.image(width, height)

const col1: rm.ColorVec = [1, 0, 0]
const col2: rm.ColorVec = [0, 1, 1]

for (let x = 0; x < width; x++) {
    const fraction = x / (width - 1)

    for (let y = 0; y < height; y++) {
        const isAboveMiddle = y < height / 2
        const color = isAboveMiddle 
            ? rm.lerpRGB(col1, col2, fraction) 
            : rm.lerpHSV(col1, col2, fraction)
        image.setPixel(x, y, color)
    }
}

image.save('lerp_rgb_hsv.png')

Clone this wiki locally