Git Product home page Git Product logo

hex-engine's Introduction

Hex Engine Logo


Website - Discord - GitHub


⚠️ WORK IN PROGRESS ⚠️

Hex Engine is a 2D Game Engine for the browser, written in TypeScript. It is designed to feel similar to React.

Hex Engine implements a variant of the popular Entity-Component-System model, adapting it to make Components radically composable. In Hex Engine, every Component is a function, and Components can call special Hook functions to define their behavior in the game engine.

Hex Engine comes out-of-the-box with a powerful development inspector and a modern frontend code compilation pipeline.

Out of the box, Hex Engine has:

  • 2D rendering via HTML5 Canvas
  • Support for sprite sheets and animations
  • Helpers for sound playback and synthesization
  • Physics (via matter.js)
  • Mouse, Keyboard, and Gamepad input
  • First-class support for popular indie game development tools Aseprite, Tiled, and BMFont
  • And much, much, more

Hex Engine is inspired by React, Impact, Unity, and LÖVE.

Here's an example of what code looks like in Hex Engine.

import {
  useDraw,
  useUpdate,
  useNewComponent,
  SystemFont,
  Label,
} from "@hex-engine/2d";

export default function MyComponent() {
  const font = useNewComponent(() => SystemFont({ name: "Arial", size: 12 }));
  const label = useNewComponent(() => Label({ font }));

  let elapsedTime = 0;
  useUpdate((delta) => {
    elapsedTime += delta;

    label.text = `Elapsed time: ${elapsedTime}`;
  });

  useDraw((context) => {
    label.draw(context, { x: 10, y: 10 });
  });
}

Hex Engine was created by yours truly, Lily Skye. I am known throughout the JavaScript Open-Source community as one of the core maintainers of Prettier, and I have also contributed to a myriad of other well-known projects, including Babel and React DevTools.

I built Hex Engine because I felt that the browser game engine space could benefit a lot from technologies and patterns found in the open-source JavaScript frontend and tooling communities.

It is the culmination of years of research, and probably the fifth or sixth game engine I have written for the browser (but the first that I've been proud enough of to share).

All that said, it's far from done- I'm hoping that we can form a community together and make something we can all be proud of.

Interested? Check out Hex Engine's homepage for installation and usage instructions.


Logo Font is Silver by Poppy Works.

hex-engine's People

Contributors

arya-s avatar coyotte508 avatar dependabot[bot] avatar emilienleroy avatar enet4 avatar mirkorainer avatar mmahandev avatar pxlbuzzard avatar sixty-nine avatar suchipi 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

hex-engine's Issues

Inspector improvements

Here are a couple of things I noticed while working on the inspector that I think could use improvements:

  • I want to see a tooltip with entity id, name, position and dimension over a hovered entity in select mode
  • I want a button to collapse all expanded entries in the inspector
  • I want selected entities in the inspector tree to be scrolled into view if they are far down the tree
  • I want the inspector controls to be fixed to the top so that I don't have to scroll a long tree back up to get to the controls
  • I want to select entities and have hover outlines even when the run loop is paused
  • I want the inspector tree entries to expand by clicking on a single element instead of being able to "fall through" holes in between (e.g. there is a gap between ▶ and the label of the entry)

Document "StorageFor" pattern

In the codebase, there are a lot of components whose names start with "StorageFor", which are used to store state or references to entities on the root entity. This pattern is generally useful when using Hex Engine, so we should add a guide to the website explaining how it works.

The idea is that you create a component that holds some state, and attach it to the root entity. Then, when you want to get that state, you use useRootEntity().getComponent.

It's a bit non-obvious in the docs right now, but the reason you're able to store state in a component and access it later is because if you return an object from a component function, getters and setters for that object's properties get copied onto the component instance, as returned by Entity.getComponent.

In this example, I model a "quest list", like you'd find in an MMORPG. The list of accepted quests is stored in the QuestListState component. The QuestListUI component renders all the quests. The AcceptQuest component adds a quest to the list.

type Quest = {
  name: string;
  description: string;
};

function QuestListState() {
  useType(QuestListState);

  const quests: Array<Quest> = [];

  return { quests };
}

function QuestListUI() {
  useType(QuestListUI);

  useDraw((context) => {
    const questListState = useRootEntity().getComponent(QuestListState);
    if (questListState) {
      const quests = questListState.quests;

      quests.forEach((quest) => {
        // use `context` to render the quest text to the screen
      });
    }
  });
}

function AcceptQuest(quest: Quest) {
  useType(AcceptQuest);

  // add a geometry component and render a button

  const mouse = useNewComponent(Mouse);
  mouse.onClick(() => {
    const questListState = useRootEntity().getComponent(QuestListState);

    if (questListState) {
      const quests = questListState.quests;

      quests.push(quest);
    }
  });
}

function Root() {
  useType(Root);

  useNewComponent(QuestListState);
  useNewComponent(QuestListUI);
  useNewComponent(() => AcceptQuest({ name: "Do the thing", description: "Yes" }));
}

To avoid needing to remember to put QuestListState on Root, you can use useNewRootComponent to initialize it the first time it is used:

const questListState = useRootEntity().getComponent(QuestListState) || useNewRootComponent(QuestListState);
const quests = questListState.quests;

Then, you can put that into a hook, and use it wherever you need the quest list:

function useQuestList() {
  const questListState = useRootEntity().getComponent(QuestListState) || useNewRootComponent(QuestListState);
  const quests = questListState.quests;
  return quests;
}

That simplifies the original code to:

type Quest = {
  name: string;
  description: string;
};

function QuestListState() {
  useType(QuestListState);

  const quests: Array<Quest> = [];

  return { quests };
}

function useQuestList() {
  const questListState = useRootEntity().getComponent(QuestListState) || useNewRootComponent(QuestListState);
  const quests = questListState.quests;
  return quests;
}

function QuestListUI() {
  useType(QuestListUI);

  useDraw((context) => {
    const quests = useQuestList();

    quests.forEach((quest) => {
      // use `context` to render the quest text to the screen
    });
  });
}

function AcceptQuest(quest: Quest) {
  useType(AcceptQuest);

  // add a geometry component and render a button

  const mouse = useNewComponent(Mouse);
  mouse.onClick(() => {
    const quests = useQuestList();
    quests.push(quest);
  });
}

function Root() {
  useType(Root);

  useNewComponent(QuestListUI);
  useNewComponent(() => AcceptQuest({ name: "Do the thing", description: "Yes" }));
}

Inspector state persists through dev server restarts

The inspector state persists when stopping and restarting the dev server in the project created by create-hex-engine-game.

If for example we pause rendering, then restart the dev server we come back to an almost blank page (just the small inspector in the top right showing frame 0). I think this is confusing and looks broken.

Support Ogmo Editor

https://ogmo-editor-3.github.io/

We should create a component or set of components that supports loading levels from Ogmo Editor.

Feature ideas:

  • Create a SpriteSheet component for the tileset(s) in the Ogmo level.
  • Create a TileMap component for each Tile Layer in the Ogmo level.
  • Create a component for each Decal Layer in the Ogmo level, that draws all the decals in that layer.
  • Create a new child entity for every Entity in the Entity Layers in the Ogmo level.
  • Support Grid Layers somehow; maybe with a Shape component? I don't know how Grid Layers work yet.

Build failing on Win10 due to 'env'

All engine packages fail with the following message on Win10 (non-WSL)

Building packages/core
packages/core: @workspace-builder/babel
'env' is not recognized as an internal or external command,operable program or batch file.

Works in WSL.

Add raycasting functions

Hex Engine should include some ray-casting and collision detection functions out of the box, that use information from the Geometry component. I haven't written them already because I don't know much about ray-casting or which formulas are commonly used to implement it effectively. I would definitely appreciate some help with this.

Need to bump the version in the `create` package?

Looks like the Point -> Vector breaking change was applied to the create package in 9f51c49 but the version for the create package hasn't been bumped + published yet, so running npx create-hex-engine-game generates a project using "@hex-engine/2d": "0.2.0" but still uses Point in the actual code.

PS - Loving everything about the engine & tooling so far 👏

Animated GIF component

It would be nice to have a component that can expose the frames of an animated GIF image using the Animation interface; I'm picturing something like this:

import { useType, useNewComponent, GIF, useDraw } from "@hex-engine/2d";
import someGifFile from "./whatever.gif";

function MyComponent() {
  useType(MyComponent);

  const gif = useNewComponent(() => GIF(someGifFile));
  gif.play(); // `gif` has the same interface as AnimationAPI

  useDraw((context) => {
    gif.drawCurrentFrame(context, { x: 0, y: 0 });
  });
}

Clarify relation of Mouse to Geometry in docs

The current docs for Mouse reads:

A Component that gives you information about where the Mouse is, relative to the current Entity, and lets you register functions to be called when the mouse cursor interacts with the current Entity.

This didn't really explain too well where it actually gets the current "area" of the entity from, especially when I'm just looking at a call like useNewComponent(Mouse), which makes it unclear how it knows that the mouse is "in bounds". I had to dig through the source a bit to know that it uses a Geometry passed to it, or a registered Geometry on the entity.

I think it would help to make it explicit in the docs that the Mouse looks for a Geometry component on the entity, and uses that to determine the entity's area.

Write Guide on creating Tic Tac Toe Game

I have created a simple Tic Tac Toe game using Hex Engine: https://github.com/suchipi/hex-engine-tic-tac-toe-example

But I don't know how to write a guide that explains concepts well and isn't just "copy this code in, then this code, then this code"...

I could use some help writing this guide.

Here's how to do it:

  • Create a new markdown file in docs
  • Add it to the "Guides" section in packages/website/sidebars.json
  • Put a link to it in packages/website/docs/guides.md

Here's how to work on it:

  • Clone the repo
  • Run yarn install
  • Run yarn start
  • Your browser should automatically open the docs site at http://localhost:3000
  • Make changes to the code, and the docs site should automatically refresh

I would appreciate help with this a ton! ❤️

Improve generated example

Hello, thank you for your interesting work. Javascript Daily lead me here.

In the example generated with the "First Time Setup", if the mouse goes out of the window, then the Box looses its physics.

Technically the fix is easy. In draggable.js:

    const onUp = () => {
        if (physics) {
            physics.setStatic(originalStatic);
        }
        isDragging = false;
    };

    mouse.onUp(onUp);
    useContext().canvas.addEventListener("mouseout", onUp);

I have tried to write some code to integrate this with the library, but whatever I try, I remain stuck because, the event listener must be attached to the canvas, or the Root component, yet it must change the state of the Box.

Something like this won't work because we attach the listener to the box.

  mouse.onUp(onUp);
  mouse.onLeave(onUp);

If I use 'mouse.onLeave()` in the Root component, what would be the right way to set the Box static?

I also tried to extend LowLevelMouse to provide more events, but then realized it might not be the right way to go. Still the event must be on the root and change the box, or more precisely the draggable...

I'd be willing to provide a concrete PR if you could provide some pointers.

Create testing library for end-users

We should create a testing library that users of @hex-engine/2d can use to test both their game as a whole as well as individual components of their game.

I'm thinking we should probably build something on top of cypress, and support image diffing snapshots.

Systems

Are there any suggestions on how to implement the "Systems" part in the ECS pattern? I read through the guides on the website but it only goes over the entity and component part.

Ogmo 3.4.0 support

Ogmo 3.4.0 came out earlier this month, and it includes some new features that we should support and some bugfixes. Notably, we should find "filepath" fields on entities and emit them in the build with URLs, and we should change the hardcoded angle units, since they fixed that bug.

To make sure we don't break existing Ogmo projects, we should check the version in the ogmo files and use the existing angle units if it's below 3.4.0.

Put a game on the homepage

Instead of the business-style scroller "why should you use this" homepage, it would be cool if there was a game built in hex-engine on the homepage, and the game itself somehow communicated the "why should you use this" information.

  • Would need to not load until the user clicked "play" or etc, so that people who are loading the site just to get to the docs don't have to wait for the game scripts to downloader
  • It'd be ideal if the inspector was present so that we can show that off. We'll probably need to tweak hex-engine-scripts so that there's a way to make a release build with the inspector included (and with function names not mangled, so you can tell what's what in the inspector)
  • The game should be playable without a keyboard or gamepad (for mobile users).

Set up CI

We need a CI setup that will run our tests on every commit and for PRs.

  • Should check types (yarn tsc --noEmit)
  • Should run jest tests (yarn jest)
  • Should run cypress tests (yarn cypress run --headed)

Note: Due to limitations with cypress screenshots, the cypress tests only work correctly when run in Electron and in non-headless mode.

Only render visible objects

Right now we draw everything even if it's off-canvas. It'd be nice if we used Geometry shape, canvas bounds, and entity transforms to determine when something is entirely non-visible, and don't draw it.

Image should support "repeat" pattern

Image should have an extra optional parameter that uses canvas image createPattern, more documentation here. This may be worth creating a new component as it could add too much complexity.

Combine Vector and Point classes into one class called Vector

The situation with Vector and Point is a little weird, and indicative that it’s been a long time since I took a math class. Point represents a generic 2-dimensional vector, which can be a point or a size, and Vector represents a 2-dimensional vector but interpreted using only angle and magnitude.

Really, the math for any 2-dimensional vector is the same regardless of if the vector represents a point, size, or “line from the origin”, so we should combine them into one class, so that APIs made out of them are more flexible.

One example of a problem that comes out of treating them as separate things when they’re really both two dimensional vectors, is that if you want to invert the vertical direction of a gamepad analog stick, and you’re using the current Vector class the Gamepad component returns, you have to convert the Vector into a Point in order to use multiplyY to flip the vertical direction, and then you probably have to convert it back into a Vector. It’s pretty silly to convert it to a Point when the thing it represents as a Point is far from a point.

To accomplish this, I’m thinking the new Vector class will have getters and setters for all of the following:

  • x
  • y
  • magnitude
  • angle

Most of its methods will be the same as the ones on Point, but the add/subtract methods that are currently on Vector will have to be named rotate instead.

This will (obviously) be a breaking change, so we will need to bump versions to 2.0 as part of this.

As long as we’re bumping to 2.0, I think we should get rid of Angle and use raw numbers for Angles. The Angle classes themselves don’t provide any value aside from being a mutable state container, but the main place this was useful was in the Rotation component that has now been replaced by Geometry.

Issue with Mouse.insideBounds & rotated geometry

I'm having trouble with Mouse.insideBounds when a light rotation is applied to the geometry.

I've tried to simplify the code as much as possible, below is an array of cards (set pixelZoom to 1 if your screen is too small) representing the hand of a player.

The cards change background to green when hovered over. The cards on the bottom right seem to work fine mostly, but the cards on the left not. You need to hover above them, at least their bottom half doesn't work.

If the rotation is removed (see comment in code), no issue at all.

import {
  useType,
  useNewComponent,
  useChild,
  Canvas,
  Vector,
  Geometry,
  Polygon,
  Mouse,
  useDraw,
} from "@hex-engine/2d";

export default function Root() {
  useType(Root);

  const canvas = useNewComponent(() => Canvas({ backgroundColor: "#444" }));
  canvas.fullscreen({ pixelZoom: 2 });

  const canvasCenter = new Vector(
    canvas.element.width / 2,
    canvas.element.height / 2
  );

  for (let i = 9; i >= 0; i--) {
    useChild(() => Card(
      new Vector(0, -800).rotateMutate(-(i-4.5) * 0.04).addMutate(canvasCenter).addYMutate(950),
      (i - 4.5) * 0.03) // Set this to 0 and the problem disappears
    );
  }
}

function Card(position: Vector, rotation: number) {
  useType(Card);
  const geo = useNewComponent(() => Geometry({
    shape: Polygon.rectangle(new Vector(40, 60)),
    position: position.clone(),
    rotation
  }));
  const mouse = useNewComponent(Mouse);

  useDraw((context) => {
    context.fillStyle = "#6666ff";
    if (mouse.isInsideBounds) {
      context.fillStyle = "green";
    }
    context.strokeStyle = "red";
    geo.shape.draw(context, "fill");
    geo.shape.draw(context, "stroke");
  });
}

I'm a bit baffled, because the cards on the middle have a very low rotation of 0.015 and the problem still happens.

ERR_OSSL_EVP_UNSUPPORTED with node 18

repro: create a new game following the first-time-setup with node v18.

log:

npm start

> [email protected] start
> hex-engine-scripts dev

Starting type checking service...
ℹ 「wds」: Project is running at http://localhost:8080/
ℹ 「wds」: webpack output is served from /
ℹ 「wds」: Content not from webpack is served from /Users/user/workspace/test
ℹ 「wds」: 404s will fallback to /index.html
Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:133:10)
    at module.exports (/Users/user/workspace/test/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:417:16)
    at handleParseError (/Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:471:10)
    at /Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:503:5
    at /Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:358:12
    at /Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at iterateNormalLoaders (/Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:221:10)
node:internal/crypto/hash:71
  this[kHandle] = new _Hash(algorithm, xofLen);
                  ^

Error: error:0308010C:digital envelope routines::unsupported
    at new Hash (node:internal/crypto/hash:71:19)
    at Object.createHash (node:crypto:133:10)
    at module.exports (/Users/user/workspace/test/node_modules/webpack/lib/util/createHash.js:135:53)
    at NormalModule._initBuildHash (/Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:417:16)
    at handleParseError (/Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:471:10)
    at /Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:503:5
    at /Users/user/workspace/test/node_modules/webpack/lib/NormalModule.js:358:12
    at /Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:373:3
    at iterateNormalLoaders (/Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:214:10)
    at Array.<anonymous> (/Users/user/workspace/test/node_modules/loader-runner/lib/LoaderRunner.js:205:4) {
  opensslErrorStack: [ 'error:03000086:digital envelope routines::initialization error' ],
  library: 'digital envelope routines',
  reason: 'unsupported',
  code: 'ERR_OSSL_EVP_UNSUPPORTED'
}

Node.js v18.7.0

workaround: export NODE_OPTIONS=--openssl-legacy-provider

reference: https://stackoverflow.com/questions/69394632/webpack-build-failing-with-err-ossl-evp-unsupported

Blocking call to window.localStorage

When the game is built in production mode, and the call to window.localStorage fails, it crashes the whole thing.

I had two encounters with this:

  • When opening the html file directly in my browser from the file system
  • When loading the game inside a sandboxed iframe
Uncaught DOMException: Failed to read the 'localStorage' property from 'Window': The document is sandboxed and lacks the 'allow-same-origin' flag.
    at Object.<anonymous> 

Create "lifecycle" doc explaining when and how often component functions are called, and how hook functions interact with frames

The docs don't make it clear that component functions only run once, when the component is created. That's different from React, so it's important to point out.

Also, for hooks that run every frame, it'd be good to add something to the docs that explains when they run, ideally with a visualization.

For the sake of writing the hooks doc later, the pseudocode is:

every frame:
  for every entity in the scene, in creation order:
    for every component on this entity, in creation order:
      run any functions registered by useUpdate, in the order they were registered

  call useCanvasDrawOrderSort, passing in every entity in the scene, in creation order. it returns an array of components, in the order they should be drawn
  for every component in the returned array:
    run any functions registered by useDraw (or useRawDraw), in the order that they were registered

NOTE: useDraw is a thin wrapper around useRawDraw.

API Proposal: useContext

What does this feature solve?

I want to be able to add components to any entity whenever I want to.

Do you have a specific use case?

I have a component called Attractor with a rotation/position attached. Every frame, it moves its entity's geometry closer until it reaches the same rotation/position.

When the game state changes, I want to be able to dynamically attach an Attractor to any of the game entities.

What would the new API look like?

The useContext function could be a method of Component or Entity, or it could be a separate funtion.

Here are some examples:

// As a method
entity.useContext(() => useNewComponent(() => Attractor(...)));
entity.getComponent(...).useContext(() => useNewComponent(() => Attractor(...)));

// As a separate function
useContext(entity, () => useNewComponent(() => Attractor(...))); 
useContext(entity.getComponent(...), (() => useNewComponent(() => Attractor(...)));

Is there an existing workaround?

For this specific use case, I can manually import instantiate from @hex-engine/core/src/instantiate.

I could also return {run: useCallbackAsCurrent(f => f())} in my components, but it's not healthy for the code.

Do you have an implementation suggestion?

function useContext<T>(instance: Instance, f: () => T): T {
  return HooksSystem.withInstance(instance, () => f());
});

Write tests for 2d

We need some integration tests for 2d. These are a little bit harder than the core tests because we'll need to verify canvas output.

Add destroy method to Entity

It would do the same thing as useDestroy().destroy; it's a convenience thing so that parent entities don't need all their children to return the destroy method out of their root component.

Typing errors when starting/building create-hex-engine-game project

Reproduction steps:

  • npx create-hex-engine-game@latest my-game
  • cd my-game
  • npm start

Errors:

TypeScript error in /Users/damien/Desktop/my-game/node_modules/@hex-engine/2d/src/Canvas/index.ts(39,20):
Property 'msUserSelect' does not exist on type 'CSSStyleDeclaration'. Did you mean 'userSelect'?  TS2551

    37 |       document.body.appendChild(canvas);
    38 |       canvas.style.userSelect = "none";
  > 39 |       canvas.style.msUserSelect = "none";
       |                    ^
    40 |       canvas.style.webkitUserSelect = "none";
    41 |     }
    42 |     canvas.className = canvas.className + " " + "hex-engine";

TypeScript error in /Users/damien/Desktop/my-game/node_modules/@hex-engine/2d/src/Canvas/polyfillContext.ts(115,11):
This condition will always return true since the function is always defined. Did you mean to call it instead?  TS2774

    113 |     resetTransform() {
    114 |       matrix = createSVGMatrix();
  > 115 |       if (super.resetTransform) {
        |           ^
    116 |         super.resetTransform();
    117 |       } else {

I worked around those by adding // @ts-ignore in both cases but there's probably a proper way to fix those 🙈

Error in webpack when building in CI

I am getting my experimental game pushed out publicly (finally). As part of that process I want to have GitHub Actions publish the game on GitHub Pages using an Action, but I am hitting an error after the build:

Creating an optimized production build...
Starting type checking service...
Using 1 worker with 2048MB memory limit

Treating warnings as errors because process.env.CI = true.
Most CI servers set it automatically.

Error: asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets: 
  main.js (300 KiB)

entrypoint size limit: The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB). This can impact web performance.
Entrypoints:
  main (300 KiB)
      main.js

I believe this can be fixed by adding a larger default max asset size to the webpack config generator.

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.