Git Product home page Git Product logo

color.js's Introduction

Color.js: Let’s get serious about color

Netlify Status npm

Official websiteContribution guide

Color.js is a color conversion and modification library originally created by two of the editors of the CSS Color specifications: Lea Verou and Chris Lilley. They continue to work on it, but are also joined by an exceptional small grassroots team of co-maintainers.

Features

  • Color space agnostic: Each color object is basically a list of coords and a color space reference. Operations are color space agnostic. Modules for a wide variety of color spaces, including Lab/LCh, OKLab/OKLCh, sRGB and friends (HSL/HSV/HWB), Display P3, Jzazbz, REC.2100 and many more.
  • Doesn't gloss over color science: Actual gamut mapping instead of naïve clipping, multiple DeltaE methods (76, CMC, 2000, Jz), multiple chromatic adaptation methods (von Kries, Bradford, CAT02, CAT16), all with sensible defaults
  • Up to date with CSS Color 4: Every CSS Color 4 format & color space supported for both input and output, whether your browser supports it or not.
  • Readable, object-oriented API: Color objects for multiple operations on the same color, and static Color.something() functions for one-off calculations
  • Modular & Extensible: Use only what you need, or a bundle. Client-side or Node. Deep extensibility with hooks.
  • Fast & efficient: Procedural, tree-shakeable API available for performance sensitive tasks and reduced bundle size

Impact

Installation

Color.js is designed make simple things easy, and complex things possible, and that extends to installation as well.

For quick experiments, you can just import Color.js directly from the CDN (kindly provided by the awesome folks at Netlify) with all modules included:

import Color from "https://colorjs.io/dist/color.js";

You can also install via npm if you’d prefer:

npm install colorjs.io

Whether you’re using NPM, the CDN, or local files, Color.js allows you to also import specific modules by directly importing from src:

  • https://colorjs.io/src/ for the CDN
  • `node_modules/colorjs.io/src/ for NPM

For example:

import Color from "https://colorjs.io/src/color.js";
import p3 from "https://colorjs.io/src/spaces/p3.js";
import rec2020 from "https://colorjs.io/src/spaces/rec2020.js";
import deltaE200 from "https://colorjs.io/src/deltaE/deltaE2000.js";

Warning: To use import statements in a browser, your <script> needs type="module"

Are you old school and prefer to simply have a global Color variable? We’ve got you covered! Just include the following script in your HTML:

<script src="https://colorjs.io/dist/color.global.js"></script>

Read more about installation

Reading colors

Any color from CSS Color Level 4 should work:

let color = new Color("slategray");
let color2 = new Color("hwb(60 30% 40% / .5)");
let color3 = new Color("color(display-p3 0 1 0 / .9)");
let color4 = new Color("lch(50% 80 30)");

You can also create Color objects manually:

let color2 = new Color("hwb", [60, 30, 40], .5);
let color3 = new Color({space: "p3", coords: [0, 1, 0], alpha: .9});

Read more about color objects

Manipulating colors

You can use properties to modify coordinates of any color space and convert back

let color = new Color("slategray");
color.lch.l = 80; // Set coord directly in any color space
color.lch.c *= 1.2; // saturate by increasing LCH chroma by 20%
color.hwb.w += 10; // any other color space also available

To modify coordinates in any color space you use color.set() and color.setAll():

let color = new Color("slategray");

// Multiple coordinates
color.set({
	"lch.l": 80, // set lightness to 80
	"lch.c": c => c * 1.2 // Relative manipulation
});

// Set single coordinate
color.set("hwb.w", w => w + 10);

Coordinates of the color's color space are available without a prefix:

let color = new Color("slategray").to("lch");

// Multiple coordinates
color.set({
	l: 80, // set lightness to 80
	c: c => c * 1.2 // Relative manipulation
});

// Set single coordinate
color.set("h", 30);

Chaining-style modifications are also supported:

let color = new Color("lch(50% 50 10)");
color = color.set({
	h: h => h + 180,
	c: 60
}).lighten();

You can also use properties:

let color = new Color("slategray");
color.lch.l = 80; // Set coord directly in any color space
color.lch.c *= 1.2; // saturate by increasing LCH chroma by 20%
color.hwb.w += 10; // any other color space also available

Coordinates of the color's color space are available without a prefix:

let color = new Color("slategray").to("lch");
color.l = 80; // Set LCH lightness
color.c *= 1.2; // saturate by increasing LCH chroma

Read more about color manipulation

Converting between color spaces & stringifying

Convert to any color space:

let color = new Color("slategray");
color.to("lch") // Convert to LCH

Output in any color space

let color = new Color("slategray");
color + ""; // default stringification
color.to("p3").toString({precision: 3});

Clip to gamut or don't

let color = new Color("p3", [0, 1, 0]);
color.to("srgb") + ""; // Default toString()
color.to("srgb").toString({inGamut: false});

Read more about output

Interpolation

Get a function that accepts a percentage:

let color = new Color("p3", [0, 1, 0]);
let redgreen = color.range("red", {
	space: "lch", // interpolation space
	outputSpace: "srgb"
});
redgreen(.5); // midpoint

Interpolation by discrete steps:

let color = new Color("p3", [0, 1, 0]);
color.steps("red", {
	space: "lch",
	outputSpace: "srgb",
	maxDeltaE: 3, // max deltaE between consecutive steps
	steps: 10 // min number of steps
});

Shortcut for specific points in the range:

let color = new Color("p3", [0, 1, 0]);
let redgreen = color.mix("red", .5, {space: "lch", outputSpace: "srgb"});
let reddishGreen = color.mix("red", .25, {space: "lch", outputSpace: "srgb"});

Static syntax (every color method has a static one too):

Color.mix("color(display-p3 0 1 0)", "red", .5);

Read more about interpolation

color.js's People

Contributors

ai avatar ambar avatar artoria2e5 avatar ayc0 avatar connormiha avatar dom1n1k avatar efergus avatar facelessuser avatar jamesnw avatar jgerigmeyer avatar jsoref avatar kleinfreund avatar kripod avatar latin-1 avatar leaverou avatar lloydk avatar manuelmeister avatar micgro42 avatar mixalistzikas avatar myndex avatar mysteryblokhed avatar nico avatar nsilvestri avatar redneb avatar sayzlim avatar sgomes avatar sidewayss avatar svgeesus avatar xi avatar xiegeo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

color.js's Issues

LCH interpolation by smaller arc? Colorspace-specific pre-interpolation transformations?

Currently we just interpolate all color components in the interpolation space with no adjustment. This means that interpolating between lch(50% 80 0) and lch(50% 80 720) produces a double rainbow.

Case in point: https://colorjs.io/demos/gradients/?color1=lch(50%25%2080%200)&color2=lch(50%25%2080%20720)

image

Should we instead interpolate by smaller arc?
When interpolating lch(50% 80 0) to lch(50% 80 360) do we want a rainbow or a solid color? And if we mod and choose smaller arc, how do we do this in a color space agnostic way? Maybe color spaces can define their own pre-interpolation adjustments or something?

ColorSpace coord accessors reject writes

> slategray 
< Color {colorSpaceId: "srgb", coords: Array(3), alpha: 1}
> slategray.chroma 
< 11.234142037623341
> slategray.chroma = slategray.chroma * 1.2
< 13.480970445148008
> slategray.chroma 
< 11.234142037623341

What happens when color changes after range function has been returned?

Consider this:

let color = new Color("red");
let color2 = new Color("black");
let gradient = color.range(color2);
color.coords[1] = 1;
color2.coords[2] = 1;
gradient(.5);

Should that interpolate with the original colors or the new colors?

Currently it interpolates the original colors:
image

Implement ΔE ITP (Delta ICtCp)

See

Pieri, E., & Pytlarz, J. (2018). Hitting the mark - A new color difference metric for HDR and WCG Imagery. In SMPTE 2017 Annual Technical Conference and Exhibition, SMPTE 2017 (Vol. 2018-January, pp. 1–13). Institute of Electrical and Electronics Engineers Inc. https://doi.org/10.5594/M001802

With the emerging demand for high-dynamic-range (HDR) and wide-color-gamut (WCG) technologies, display and projector manufacturers are racing to extend their color primaries in the cinema and in the home. With these brighter and wider colors, the question is: in calibration, how close is close enough? This answer is increasingly important for both the consumer and professional display/projector market as they balance design trade-offs. With HDR/WCG technology, an increasing issue is that many of the color difference metrics in common use today, such as ΔE00, substantially deviate from human perception and become unreliable for measuring color differences. This causes under and over prediction of color differences and can to lead to sub-optimal design decisions and difficulties during calibration. There is a large amount of perceptual color difference data in the field today, however the majority was collected using reflective surfaces and very little reaches the boundaries of modern display capabilities. To provide a better tool for facilitating design choices, this paper will present a 'ground truth' visible color difference dataset. These visual experiments were conducted between luminance levels of 0.1 and 1000 cd/m2 on high dynamic range laser cinema projectors with approximate BT.2100 color primaries. We will present our findings, compare against current metrics, and propose specifying color tolerances using the ΔICTCP metric for HDR and WCG imagery.

ICtCp reverse transformation to XYZ underspecified, may be unstable

The Dolby whitepaper on ICtCp defines two forward transforms (one from Rec.2020 linear light, one from Absolute D65 XYZ) but the reverse transform is left as an exercise for the reader.

I see an issue claiming the inverse is not numerically stable and another one.

The latter link includes Matlab code for the inverse EOTF:

EOTF = @(PQ) (max(PQ.^(32/2523)-(3424/4096),0) ./ ... ((2413/128)-(2392/128)*PQ.^(32/2523))).^(16384/2610)*10000;

Avoid hard-coded linear Bradford CAT

We currently have

static chromaticAdaptation (W1, W2, XYZ) {

with tests for W1 and W2 being the D50-D65 pair in either direction. If true, a linear Bradford CAT is used. If false, there is a hook and if the hook is not used, a TypeError is thrown.

This makes it difficult to use a non-Bradford CAT for that common pair of whitepoints.

Instead, chromaticAdaptation should take an optional fourth parameter, method with a default of "Bradford" and tests like

if (W1 === Color.whites.D65 && W2 === Color.whites.D50) {

would become

if (W1 === Color.whites.D65 && W2 === Color.whites.D50 && method = "Bradford") {

to allow the flow of control to pass through to the hook.

Sensible defaults for deltaE

Currently, deltaE modules can add new deltaE methods that can be used via the method parameter in color.deltaE(). The default method is determined via Color.defaults.deltaE.

However, it's a reasonable assumption that if someone imports e.g. DeltaE 2000, they expect this superior DeltaE to be used as the default. Currently, unless they override Color.defaults.deltaE, DeltaE 76 will be the default.

We could make it so that DeltaE modules override the default (you can still set it yourself by setting Color.defaults.deltaE). This does mean that importing e.g. the DeltaE 2000 module, you also set your default DeltaE algorithm to DeltaE 2000.

However, this has a bunch of drawbacks:

  • These superior color difference algorithms are also slower, so you may still want to use DeltaE 76 as a default.
  • Which default you end up with depends on the order of imported modules, since each of them is going to override the previous default.
  • You may even want a different default depending on application. E.g. you may want CMC or 76 in general, but 2000 in Color.steps() since 2000 is better at small color differences.

Bug in sRGB to HSL conversion

At first I thought this was an interpolation bug, because I noticed it when using outputSpace: "hsl" for my comment in w3c/csswg-drafts#4735 (comment) .

However, it looks like it's a conversion bug, as it happens when just converting standalone sRGB colors to HSL. Converting in-gamut sRGB colors to HSL should not affect the color, yet it seems to do so.

image

(Note to self: Need some sort of sharing Color Notebook snippets that doesn't involve screenshotting!)

CAT02 (and CAT16) implementation assumes complete adaptation

The full model of CAT02 and CAT16 requires:

  • LA, the luminance of the adapting white
  • F, a correction factor for the luminance of the surround
  • D, the degree of adaptation to the current white point

our implementation assumes D = 1.0 (full adaptation), F = 1.0 (surround is not dim or dark) and thus LA is not used. Note that Hunt & Pointer "Measuring Color" p. 132 suggests a default value for D (in the absence of more precise values) of 0.95, not 1.0.

This is likely sufficient, but does mean that users who want to use partial adaptation need to look elsewhere.

It would be possible to add support for this, with an optional object literal of those three parameters and a more complex calculation of the eventual matrix M if they are present. The calculations are in Hunt & Pointer pp. 131-133, with two worked examples on pp.140-141. Chapter 9 of Fairchild "Color Appearance Models" is also helpful.

[color notebook] Live playground for sharing snippets

Snippets can be saved as gists or in Github repos, as Markdown.
This way, people can share entire Color notebook documents if they so desire.

Can be implemented with Mavo and a little bit of custom JS for saving a new Gist.

JzAzCz to JzCzhz is wrong

Conversion functions literally return the input for Jz, yet we have different values

return [
    Jz, // Jz is still Jz
    Math.sqrt(az ** 2 + bz ** 2), // Chroma
    angles.constrain(hue) // Hue, in degrees [0 to 360)
];
return [
    jzczhz[0], // Jz is still Jz
    jzczhz[1] * Math.cos(jzczhz[2] * Math.PI / 180), // az
    jzczhz[1] * Math.sin(jzczhz[2] * Math.PI / 180)  // bz
];

image

Support interpolation between multiple colors

If the color argument to the interpolation functions is an array, we should interpolate between all these colors.

This brings up some interesting challenges:

  • Does steps apply to the transition between each pair of colors or all of them? If the latter, what happens e.g. when we're interpolating between 3 colors in e.g. 4 steps? We don't get our second color at all?
  • Does range also accept an array of colors? If so, that means you can't just get 0 and 1 to read all color stops, because there could be any number of stops. Perhaps a property in the function stores the color stops?
  • If range also accepts an array, do we need to be able to specify positions of the color stops too?

Non-linear interpolation

Right now we only support linear interpolation.

We should also support:

  • Custom cubic bezier
  • Possibly keywords
  • Entirely custom functions

Support hwb

Since we claim to support "every CSS Color 4" format, we should support this too.

Finish smarter gamut mapping with clip?

Currently the default method for gamut mapping is reducing lch chroma by binary search until the color is very close to being in gamut (thanks @tabatkins).

However, this sometimes produces colors that are very slightly out of gamut. E.g. take a look at https://colorjs.io/apps/convert/?color=color(display-p3%200%201%200)&precision=5 which returns rgb(-0.001% 98.388% 16.188%).

I wonder if it would be a good strategy to finish off the smarter gamut mapping with clipping, so that we get rid of these epsilons above and below?

Midpoint values can be negative or greater than 1

The documentation says that range()returns a function that "accepts a percentage as a 0 - 1 number".

It turns out that the number can be negative or greater than 1, and the function returned by range() will try to find a value that matches it, even if it is outside the color range.

let color = new Color("#FF0000");
let redgreen = color.range("#00FF00");
redgreen(0).toString({inGamut: false});   // rgb(100% 0% 0%)
redgreen(1.0).toString({inGamut: false}); // rgb(0% 100% 0%)
redgreen(-1).toString({inGamut: false});  // rgb(119.23% -543.12% 10.585%)
redgreen(2).toString({inGamut: false});   // rgb(-2,296.1% 157.96% 31.987%)

[color-notebook] Execute in sandbox

Currently variables are local to each code snippet but the Color object they run on is the same as the rest of the page, so any global modifications (e.g. to Color.defaults) affect other snippets too.

Also, when we have a playground (see #29) this will be insecure as well.

Good default color space for interpolation?

Currently, we use the same heuristic as I proposed for CSS: If both colors are in the same color space we interpolate in that color space, otherwise we interpolate in Lab.

However, in CSS we need this for backwards compat, whereas here we don't have the same issue. Furthermore, since many, many colors are defined via sRGB (as the most common formats are sRGB), this results in a lot of crappy interpolation, and most people don't know better.

In addition to this, as @svgeesus pointed out, I'm not sure Lab is a good default space either. Perhaps LCH is better, though to use LCH as a default, we would need to do mod and smaller arc (see #15).

Achromatic interpolation

d3-color seems to handle L=0 and C=0 specially, by

Now achromatic colors in HCL have undefined hue (NaN), and black has undefined chroma. And for white, L = 100, C = 0 for HCL, and A = B = 0 for Lab.

This produces more reasonable results when interpolating in LCH, but:

  • As one can see in the last bit, you still have to be explicit for white
  • This produces discontinuities at values very close to 0 but not 0.

Related: w3c/csswg-drafts#4647 (comment)

Precision is hard to understand in convert app

in this example precision is set as 4, and the LCH value is color(lch 85.9 140.11 135.6).

  • The Lightness has 3 digits of precision, the Chroma has 5 and the Hue has 4. Maybe there is a reason for this, in which case it should be explained in the docs, or maybe this is a bug in the precision code.

  • Also, the CSS Color 4 color() function accepts Lab, as requested by Simon Fraser, but does not accept LCH

  • Also, the color() stringification omits percent for LCH Lightness (presumably because the CSS Color 4 color() function used to only allow but should use percent.

max-ΔΕ interpolation is buggy

Right now our algorithm is:

  • Produce number of steps based on steps
  • Iterate over steps pair-wise, calculate ΔΕ. If > delta, insert midpoint, repeat.
  • This results in a lot of problematic boundary conditions, where some consecutive pairs have roughly delta/2 difference and others slightly below delta.

Check this out:
image

A better algorithm might be:

let totalDelta = color1.deltaE(color2)
let actualSteps = Math.max(steps, Math.ceil(totalDelta / delta))
// Then return an evenly spaced range of actualSteps steps

Problem: How does this work with non-linear-interpolation #12?

Alternatively, we can keep the current algorithm, but every time we determine we need to add a midpoint between two stops, we do it for the entire array, then rinse and repeat. This may be easier to adapt to non-linear interpolation.

Color.prototype.set()

This would allow setting multiple properties at once and return the color instance, to enable chaining.

  • Single-property syntax:
color.set("lightness", 50).set("chroma", 100);
  • Object literal:
color.set({
  lightness: 50,
  chroma: 100
});
  • It should also allow relative manipulations:
color.set({
  lightness: "* 1.2*",
  chroma: c => c * c // custom math via a function!
});
  • And deeply nested properties (this will become even more useful after #16):
color.set("hsl.lightness", 50);

Setting color[spaceId][i] does nothing

E.g. color.hsl.hue = 0 does not actually modify the color.
A logical consequence of the fact that this is just an array generated and returned by a getter, but still confusing AF. Will likely be fixed in one fell swoop along with #16.

Convert app could show OOG more clearly

In the convert app, if you select a color out of gamut, OOG is detectable because the color.toString() and Color.prototype.toString.call(color) values are not the same.

It would be good to more clearly indicate that, perhaps a salmon pink instead of pale grey background on the color.toString() column, mainly to avoid people blindly copy and pasting the values without realizing gamut mapping has occurred.

A brief paragraph of introductory text might also help.

API to check if in gamut, and get both coords

Questions to answer:

  • How to check whether the color is in gamut?
  • Should this.coords be clipped to gamut?
    • If not, how to get clipped coords?
    • If so, how to get original coords?

("Clipped" here doesn't refer to per-component clipping, but chroma reduction until we're in gamut)

We should also provide a way for custom rendering intents, but maybe that's another issue.

Syntax for getting coordinates of various color spaces

Right now, the algorithm is as follows:

  • Coordinate names are added as properties to Color.prototype if there are no properties with such names already.
  • If there are clashes, the color space id is prepended. So, when adding HSL, hue and lightness conflict with LCH which is added earlier, so each color has hsl_lightness, saturation amd hsl_hue properties.

This is obviously problematic.

  • It's non-deterministic, as it depends on the order of color spaces. However, there should be a way to "privilege" certain color spaces. I.e. we do want chroma, hue, and lightness from LCH on every color, and we don't really want those to be as easily accessible for HSL.
  • It's inconsistent even within the same color space, as some coords may be prefixed and others not. This is terrible
  • Underscore_case is not common within JS, but camelCase here would make things hard to read.
  • There are way too many properties on every object, even for the few default color spaces. We need to be able to load a ton of color spaces without making Color objects super heavy.

Potential solutions:

  • Hang the properties on the coord accessors instead of the color instances (e.g. color.hsl.lightness. This does read nicely, but it means we can't just return the coords array as-is when someone uses the coord property that corresponds to the current color space (i.e. when we have an sRGB color, we can't just return this.coords for color.srgb)
  • Instead of properties, have a special syntax for getting coords by name, e.g. `color.get("hsl.lightness"). This is what chroma.js does
  • Have accessors for the current color space and a few privileged ones (possibly opt-in on the color space level). This is kinda what D3 Color does, it only has accessors for current color space, but it uses subclasses. We want to be able to change the current color space at will, so this approach would make that more expensive, but not a dealbreaker.
  • If we keep the prefixed accessors, we should make sure either ALL coords get prefixed, or none of them.

Interpolation

API sketch:

  • color.steps(color2, {space, delta, steps, maxSteps})

  • Color.steps(color1, color2, {colorSpace, delta, steps, maxSteps})

  • If space is provided, it's used for the interpolation. If not, then a) if both colors have the same colorSpace, it's used for interpolation, otherwise Lab is used.

  • If delta is provided, the number of colors generated is such that the distance between any two consecutive ones does not exceed delta. maxSteps is used as a failsafe, to avoid getting way too many colors.

  • If steps are provided, the colors returned are at least that many (if delta is also provided they may need to be more)

Use NaN for undefined coords

This will resolve #14 and #23.

If polar space colors are created, we should preserve their coordinates, but if we are converting to them, we should use NaN for coords that are undefined (let's start from hue for achromatic colors, not sure if this is needed for chroma/saturation).

This does mean that roundtripping an achromatic color would lose its hue data, but I don't think that's a significant problem.

Tasks needed:

  • 1. Handle NaN values in interpolation (interpolating with NaN returns the other coord)
  • 2. Output NaN for hue when converting achromatic colors to any color space with hue
  • 3. Output NaN as 0 in toString() since it needs to produce a valid color
  • 4. Handle NaN in deltaE (same as interpolation I guess)
  • 5. Handle NaN in color contrast
  • 6. Handle NaN in color conversions
  • 9. Handle NaN anywhere else

Avoid hard-coded D50 in XYZ and Lab definitions

Use case 1:

Recently, I was trying to convert some published experimental data from CIE LCH (with an Illuminant C whitepoint) to CIE LCH (with the standard D50 whitepoint).

The required chain of conversions is:
LCH → Lab → XYZ(with C whitepoint) → CAT16(C to D50) →Lab → LCH

This involved copying the toXYZ(Lab) and fromXYZ(XYZ) functions and changing the hard-coded D50 whitepoint.

Use case 2:

The Jzazbz and JzCzHz colorspaces require input data as absolute (rather than relative) D65 (rather than D50) XYZ. The current implementation accepts relative D50 XYZ as input and performs a Bradford CAT to D65 before scaling the values to the recommended SDR media white luminance

Enhancement

To facilitate similar tasks, it would be better if these functions allowed an optional whitepoint to be passed (retaining D50 as the default) so the existing functions could be reused without copy-pasting.

ICtCp known values

Lacking a reference converter, this graphic at least gives rough estimates for Rec.2020 to ICyCp conversion.

image

Adaptation documentation needs edits

The inline examples in the Chromatic Adaptation documentation are not correct.

The first two, I tried to demonstrate XYZ Scaling and the von Kries adaptations, but found I could not because our code has a non-optional Bradford to D50 as soon as you convert to XYZ.

The example in Using CATs looks correct, but has type errors.

I wanted to add a second example showing how to override the default Bradford with another method, but didn't understand the hook well enough to create the example.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.