Giter Club home page Giter Club logo

egui_node_graph's Introduction

Egui Node Graph

There you have it! Now go build your next awesome node graph thing in Rust ๐Ÿฆ€

Latest version Documentation MIT unsafe forbidden

ARCHIVED REPOSITORY This repository is now archived because of a variety of technical and political reasons that made me loose my motivation to continue contributing to the Rust community in my free time.

Showcase image

Egui node graph is a featureful, customizable library to create node graph applications using egui. The library takes care of presenting a node graph to your users, and allows customizing many aspects of the interaction, creating the semantics you want for your specific application.

Features and goals

This crate is meant to be a solid base for anyone wanting to expose a node graph interface to their users. Its main design goal is to be completely agnostic to the semantics of the graph, be it game logic, audio production, dialog trees, shader generators... we have you covered!

The purpose of this library is to draw your graphs and handle the common user interaction, like adding new nodes, moving nodes or creating connections. All the additional functionality is provided by the user by means of custom user types implementing several traits.

Usage

To see a node graph in action, simply clone this repository and launch the example using cargo run. This should open a window with an empty canvas. Right clicking anywhere on the screen will bring up the node finder menu.

The application code in the example is thoroughly commented and serves as a good introduction to embedding this library in your egui project.

A note on API visibility

Contrary to the general tendency in the Rust ecosytem, this library exposes all types and fields that may be remotely relevant to a user as public. This is done with the intent to be as flexible as possible, so no implementation details are hidden from users who wish to tinker with the internals. Note that this crate forbids use of unsafe so there is no risk of introducing UB by breaking any of its invariants.

That being said, for the most typical use cases, you will want to stick to the customization options this crate provides for you: The generic types in the GraphEditorState object and their associated traits are the main API, all of the other types and fields in this crate should be considered an implementation detail. The example project contains a detailed explanation of all the customization options and how are users supposed to interact with this crate.

Finally, this does not change the fact that this crate follows semantic versioning, as is usual in the Rust ecosystem. Any change to a public field is still considered breaking.

Use cases

Egui node graph is the library powering the graph user interface of Blackjack, a 3d procedural modelling software built in Rust using egui, rend3 and wgpu. Main interface of blackjack Are you using this crate for something cool? Add yourself to this section by sending a PR!

Contributing

Contributions are welcome! Before writing a PR, please get in touch by filing an issue ๐Ÿ˜„

egui_node_graph's People

Contributors

bpostlethwaite avatar fenollp avatar fkaa avatar gmorenz avatar hakolao avatar huisedenanhai avatar issew avatar kamirr avatar kkngsm avatar mathiaspius avatar matthijsjanssens avatar nodespace avatar philpax avatar setzer22 avatar veykril 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

egui_node_graph's Issues

Hide close button of node widget

I'm working on my render graph editor. There are two special nodes called Input and Output. They should exist for all my node graphs, can not be created by the user, nor can be deleted.

I want to hide the close button for my Input/Output node. Currently I found no existing way to do this.

There can be a method fn can_delete(&self) -> bool for NodeDataTrait, so the nodes can decide whether it is deletable.

I implemented it on my fork. I can make a pr if you like.

Suggestion: Read-only mode

Right now, the node graph can always be edited by the user. I think a read-only mode could be a nice feature to have. I could for example allow nodes to be moved around while preventing the user from editing connections or removing a node.

A use case for this functionality could involve enabling users to modify the values within the nodes of a generated computation flow.

I feel like it could be related to #6 to some extent.

missing input on Connection Only and then Connection/Constant node

so i made a node

MyNodeTemplate::JoinString => {
                graph.add_input_param(
                    node_id,
                    "join".to_string(),
                    DataType::String,
                    ValueHolder::String {
                        value: " ".to_string(),
                    },
                    InputParamKind::ConnectionOrConstant,
                    true,
                );
                graph.add_input_param(
                    node_id,
                    "strings".to_string(),
                    DataType::List,
                    ValueHolder::List { value: vec![] },
                    InputParamKind::ConnectionOnly,
                    true,
                );
                graph.add_output_param(node_id, "output".to_string(), DataType::String);
            }

as you can see it should have 2 node inputs yet when i run the example
image
it only has 1 node input instead of 2
I tried swapping the order of the join and strings arguments yet it never shows the connection for the strings

Add api to remove param's from nodes

Currently we can add parameters, but not remove them. It would be nice to be able to do both, to be able to do things like have math nodes that take a variable number of inputs, or to be able to grow/shrink the number of ouputs so that there's always another free one (for... reasons).

I've implemented this for output param's in 96dc5b6 - which depends on #30.

Performance concerns

Hey! I wanted to use this library for my project, but it seems it's not very performant at high node counts. I noticed my computer struggling at 200 (unconnected) nodes, and it basically slowing to a standstill at 400.

If it's possible to add some performance features for large graphs, that would be appreciated. Not rendering nodes that aren't on-screen would be a logical place to start. (Though with the connections running across the screen that might be difficult.

Suggestion: add functional part to example

For now as I understand the example is only for displaying the nodes visually. Would nice to have also demonstrated how to connect the functional part. Like doing the adding, substraction, etc.

Consider allowing properties to be both inputs and outputs at the same time

Right now a Property can be either an Input or an Output, but not both. We should consider the option of allowing a property to be both an input and an output. This is used in the blueprint editor of UnrealEngine to declare the control flow of the script:
Blueprint example
To achieve this, we could unify InputParam and OutputParam structs, and instead differentiate between inputs and outputs in a new ParamKind enum (which would have many possible values, like InputOnly, OutputOnly, InputOutput, InputOrConstant(ValueType), etc).

Incorrect draw order

I have two, possibly related, issues with UI ordering

1. Cables are above nodes

Cables should render behind nodes. As you can see, if they are in front, it can look pretty janky.
Screenshot 2022-11-12 at 1 41 47 PM

2. Attempting to select a node selects most back node when layered

If I click on the title "ObjRender", it brings the "Expression" node to the front.
I expect the UI to reflect what I can interact with. "Expression" should only be selected if I click where I can see it, on the left.
Screenshot 2022-11-12 at 1 50 47 PM

Suggestion: right-click popup should not be this library's concern

Right now when the user right-clicks on the graph editor, the "add new node" popup appears. I think this is something that should be done in application code, instead of library code. I can see a few situations in which a programmer would want to have a specific context menu:
1- When clicking on a node, I might want a menu that allows me to delete, rename, or clone it, or do other fancy stuff.
2- When clicking on the background, I might want a menu that changes depending on the application state. i.e: I can start or stop an animation by having the animation controls in the popup menu, and what the controls show would change every frame.

To achieve this, the library should simply return a "right click event" and let the programmer decide what to do with it. Ofc we should add an example on how to use that event.

I will toy with the code today and see if I can achieve something.

Persistence

It would be helpful to know how to use the "persistence" feature. I presume it uses serde to store state to persistence storage, but how to use it?

Fix some QoL issues introduced by #19

Changes in #19 introduced a few new edge cases that need to be accounted for:

  • Right now, mouse controls for graph panning (middle mouse drag) are handled everywhere. We should restrict that to be inside the graph's Ui.
  • The 'new node' dropdown is not clipped to the graph's ui contents.
  • The 'new node' dropdown sometimes draws before the window containing the graph, making it invisible.

None of this affects the original mode of operation, where the graph was being drawn as a full-screen CentralPanel.

Add topological iterator to Graph class

Iterating through all nodes in a graph does currently not ensure that all inputs of a node have been generated.
To simplify the generation of all values in a graph, an iterator that provides a topological ordering could be created.

Infinite loop countermeasure

If you build the nodes in this way and click on "๐Ÿ‘ Set active" button, the application will crash.
image

How about providing a way for the library to avoid crashing if the loop is accidentally created?

For example, if I loop in blender, it looks like this and will not compute.
image

What is the best way to find all final nodes ?

Discussed in #13

Originally posted by rsaccon February 25, 2022
I want to find all final nodes, which is all nodes which do not have out-going connection. I haven't found any in-built way at egui_node_graph to do this.

What I found is a way to find a connection based on the InputId, by using:

egui_node_graph::graph_impls::Graph
    pub fn connection(&self, input: InputId) -> Option<OutputId>

But with that approach, to find out for one specific node, whether it has no outgoing connections, I would have to iterate over all inputs of all nodes to query if they have a connection to that specific node, which is not very efficient. Is there a better way to do this ?

If there is no good way to do this yet, I suggest we add that capability to egui_node_graph by maintaining a list of final nodes (which needs to be adapted each time a connection gets added or removed).

Add "CreatedNode" NodeResponse

I was just messing around with your example and it looks really cool! For my intended use case I need to keep some external data structure up to date with your graph editor. For this use case a "CreatedNode(NodeId)" NodeResponse would be beneficial. Just tried to clone the repo and add it myself, and that was really simple :)

Reverse order of id's in `iter_connections`

iter_connections current returns an iterator of (InputId, OutputId). I believe it would be more consistent to return (OutputId, InputId), and it matches the direction of connections in the graph.

On the other hand it's a bit of an unnecessary breaking change, but it's caught by the type system, and I think this library is young enough that it's worth it.

This was inspired by @philpax 's comment here. I did this here 32aa8d3, but it will conflict with #30, so waiting on that to make a PR.

Sorry about how inter-connected all my proposed changes are ๐Ÿ˜†

Handling disconnection events on node deletion is a bit cumbersome

I need to track the changes to the UI graph and apply them to some data model. However, the input and output IDs of the connections don't tell me much, I really need to know the indices within the connections (as in, was it connected to the 2nd input or the 1st output, etc.). I think the only way I can look those up (apart from doing separate bookkeeping) is to query the nodes in question and then look up the id in their list of inputs and outputs, respectively.

But then I run into a problem with deleted nodes, because by the time you're handling the NodeResponse, the node and their input and outputparams are already removed from the graph.

I did some digging through the source code, and it seems the order of events is as follows:

DeleteNodeUi
<potentially some other events>
any number of DisconnectEvents (generated by DeleteNodeUi)
DeleteFullNode (generated by DeleteNodeUi)

Now, the DeleteFullNode contains the original Node, but I really like to know that during the DisconnectEvents caused by the removal of that node.

Of course there are some ways around this. I already mentioned separate bookkeeping, but one could also query whether the node exists during response handling, find out that it doesn't, and then queue up those events so you can process them when the the DeleteFullNode for the respective node comes around.

I don't see a good solution from the API side though. Perhaps some shadow storage that keeps the data around until the next update? In any way, it might be a good idea to mention in the comments that the IDs as reported by the events might be stale.

Consider using lambdas instead of macros for the example

Hey there! I'm building off the example to work for my use case, and I noticed you're using macros to construct the node parameters:

macro_rules! input {
    (scalar $name:expr) => {
        graph.add_input_param(
            node_id,
            $name.to_string(),
            MyDataType::Scalar,
            MyValueType::Scalar { value: 0.0 },
            InputParamKind::ConnectionOrConstant,
            true,
        );
    };
    // ...
}

macro_rules! output {
    (scalar $name:expr) => {
        graph.add_output_param(node_id, $name.to_string(), MyDataType::Scalar);
    };
    // ...
}

// ...
match self {
    MyNodeTemplate::SubtractScalar => {
        input!(scalar "A");
        input!(scalar "B");
        output!(scalar "out");
    }
}

I'd suggest using lambdas, because they're easier to parameterise and (in my opinion) cleaner:

let input_scalar = |graph: &mut MyGraph, name: &str| {
    graph.add_input_param(
        node_id,
        name.to_string(),
        MyDataType::Scalar,
        MyValueType::Scalar { value: 0.0 },
        InputParamKind::ConnectionOrConstant,
        true,
    );
};

let output_scalar = |graph: &mut MyGraph, name: &str| {
    graph.add_output_param(node_id, name.to_string(), MyDataType::Scalar);
};

match self {
    MyNodeTemplate::SubtractScalar => {
        input_scalar(graph, "A");
        input_scalar(graph, "B");
        output_scalar(graph, "out");
    }
}

Suggestion: there should be no NodeTemplate mechanism

Having a compile-time list of possible node types can be limiting for some use cases, because one might need "dynamic types" of nodes.
A few use cases that come to my mind:
1- I might want to implement the possibility for my user to select a bunch of nodes, group them, and create a "new template" that, to my application logic, is equivalent of all the grouped nodes.
2- I might want to have plugins for my application, and the plugin would register new node types.

Since any node always contains the user-provided NodeData generic, the application can use that struct/enum to differentiate between all the possible operations that a node represents.

The node finder should use `button_released(MouseButton::Secondary)` when available

In #40, a change was introduced where the check to spawn the node finder used mouse.button_released instead of mouse.secondary_down(). This is the more correct behavior, as you only want to spawn the finder once the click is complete. Unfortunately, this is only available on egui master, not 0.18, so this change cannot be applied until egui 0.19 is released.

I'm leaving this issue as a reminder

Cannot use more than one node_graph in an egui app

NodeIds use slotmaps to hold references. Slotmaps return the same sequence of IDs for a given KeyType.

For example this runs with no panics

use slotmap::SlotMap;

fn main() {
    let mut sm1 = SlotMap::new();
    let mut sm2 = SlotMap::new();
    let sm1_key1 = sm1.insert("foo");
    let sm2_key1 = sm2.insert("bar");

    assert_eq!(sm1_key1, sm2_key1);
}

Egui Node Graph statically assigns the same KeyType to the node_graph slotmap and therefore multiple node_graphs in the same process will return the same keys. Since these keys go into the same "global" egui widget key-space egui complains about identical keys.

Delete the active node in egui_node_graph_example, App will crash

This is a great library ๐Ÿ˜. Thank you!

I will report it because egui_node_graph_example crashed.

Operation until the crash

  1. Create any Node.
  2. Set Active the Node.
  3. Delete the Node
  4. Crashed!!
thread 'main' panicked at 'NodeId index error for NodeId(1v1). Has the value been deleted?', /egui_node_graph/egui_node_graph/src/index_impls.rs:33:1
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Reason for the bug

This is because MyResponse::SetActiveNode(node) holds the NodeId even if the node is deleted, and uses that NodeId to access the deleted Node in the evaluate_node function.

How to fix

if let Some(node) = self.state.user_state.active_node {
let text = match evaluate_node(&self.state.graph, node, &mut HashMap::new()) {
Ok(value) => format!("The result is: {:?}", value),
Err(err) => format!("Execution error: {}", err),
};
ctx.debug_painter().text(
egui::pos2(10.0, 10.0),
egui::Align2::LEFT_TOP,
text,
egui::TextStyle::Button,
egui::Color32::WHITE,
);
}

if let Some(node) = self.state.user_state.active_node {
+    if self.state.graph.nodes.contains_key(node){
        let text = match evaluate_node(&self.state.graph, node, &mut HashMap::new()) {
            Ok(value) => format!("The result is: {:?}", value),
            Err(err) => format!("Execution error: {}", err),
        };
        ctx.debug_painter().text(
            egui::pos2(10.0, 10.0),
            egui::Align2::LEFT_TOP,
            text,
            egui::TextStyle::Button,
            egui::Color32::WHITE,
        );
+    }
}

Passing 'Ui' to draw_graph_editor

Currently draw_graph_editor creates a CentralPanel to draw its stuff. In my case I would prefer if the the main application manages the container for for the graph_editor and just passes a Ui to draw_graph_editor. Is there a specific reason for the CentralPanel approach (like special requirement for the pan-zoom) ?

Right click output - create node and connect

If you right click a node output, it should show the add node ui, then automatically connect the added node to the clicked output if compatible.

I'm happy to pick up this work.

Visual Enhancement: Bezier Curves

Hi,

first of all, this library is a great starting point for anyone wanting to do nodegraphs. So thanks for releasing it!

The Editor currently uses straight lines to connect the nodes. But Bezier Curves look so much nicer :)

Luckily egui is adding support for cubic bezier curves.
But since this is sadly only in their git, I am doing this writeup so once it ships, the implementation is straightforward.

Here I have linked a demo on how bezier curves can be set up to look like they do in other editors. You can move the endpoints to see how it feels. Notably, the control points are half the distance (s in the demo) away from the endpoints, but on the same y coordinate.

So, what has to be done here in the node graph?

In egui_node_graph/src/editor_ui.rs; Instead of:

painter.line_segment([src_pos, dst_pos], connection_stroke);

Something like:

let s = 0.5 * src_pos.distance(dst_pos);

let control_pos1 = pos2(src_pos.x + s, src_pos.y);
let control_pos2 = pos2(src_pos.x - s, src_pos.y);

painter.add(Shape::CubicBezier(CubicBezierShape {
    fill: Color32::TRANSPARENT,
    points:[src_pos,control_pos1,control_pos2,dst_pos],
    closed: false,
    stroke: connection_stroke,
}));

Change UserState to outside of GraphEditorState

In some cases, you do not want to implement serde in UserState but want to pass it as an argument to bottom_ui().
However, currently, if the user implements serde in GraphEditorState, the user must also implement it in UserState.

Therefore, we suggest not keeping UserState in GraphEditorState.

Like this,

draw_graph_editor(ui, AllMyNodeTemplates, &self.user_state)

Zoom in/out supported by default (like in blackjack)?

Hello, first of all thank you for extracting this amazing library from blackjack and making it available!

I was wondering if I might have missed the support for zooming in and out of the node graph, like possible in blackjack?
I see panning being implemented by default by pressing middle mouse button and moving, but I don't see any handling for the scroll wheel in the code.

When changing the scroll value of the PanZoom struct manually, there does not seem to be any visual feedback of a change. Is zooming supported as of now and if not, can it be ported over from blackjack, too?

I could also work an a PR to port this functionality, but since I don't know the blackjack code well I would not know where to start.

Port Snapping doesn't take InputParamKind::ConstantOnly into account

I made a node that has one input that is InputParamKind::ConstantOnly. If I try to click near an output port of the same type as the input port I crash with the following:

at egui_node_graph-9cc9145316a46463\eeecd63\egui_node_graph\src\editor_ui.rs:189
       187 โ”‚                     .iter()
       188 โ”‚                     .find_map(|(port_id, _)| {
       189 >                         let port_pos = port_locations[&port_id.into()];
       190 โ”‚                         if port_pos.distance(cursor_pos) < DISTANCE_TO_CONNECT {
       191 โ”‚                             Some(port_pos)

I can connect from an input back to the output without issues.So it appears that .find_map from line 188 does not exclude port_ids that do not have port_locations associated with them such as the InputParamKind::ConstantOnly, which should be filtered out prior to the mapping of existing locations.

Easy way to create pre-existing graph state?

Is there an easy way to create a graph state before running the application, either by loading from a file or just writing code. The example doesn't provide a clear way to just populate nodes from the outset (rather than drawing).

Buttons drawn on (after) the graph doesn't working.

fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
egui::TopBottomPanel::top("top").show(ctx, |ui| {
egui::menu::bar(ui, |ui| {
egui::widgets::global_dark_light_mode_switch(ui);
});
});
let graph_response = egui::CentralPanel::default()
.show(ctx, |ui| {
self.state
.draw_graph_editor(ui, AllMyNodeTemplates, &mut self.user_state)
})
.inner;

If change it as follows, the switch doesn't work.

fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
    let graph_response = egui::CentralPanel::default()
        .show(ctx, |ui| {
            self.state
                .draw_graph_editor(ui, AllMyNodeTemplates, &mut self.user_state)
        })
        .inner;
    egui::TopBottomPanel::top("top").show(ctx, |ui| {
        egui::menu::bar(ui, |ui| {
            egui::widgets::global_dark_light_mode_switch(ui);
        });
    });

Remove Widget on input connection

This is less of an issue more of a question:

I tried for a while to find a way to "bind" inputs to a widget in order to emulate the behaviour in Blender's node editor. In Blender it behaves as follows:

  • each Widget has a corresponding input
  • if you connect something to that input, the widget is hidden and the set value ignored
  • if you disconnect that input the widget is shown and the previously set value restored

I suspected the way to do this is to remove the widget when the onConnectionsChange callback fires, however I did not find a removeWidget method that I could have used to remove that widget. Any pointers are welcome.

Cant Compile to web

Im facing this error when i try to compile the node editor to web:
error[E0061]: this function takes 3 arguments but 2 arguments were supplied
--> egui_node_graph_example\src\lib.rs:22:5
|
22 | eframe::start_web(canvas_id, Box::new(app))
| ^^^^^^^^^^^^^^^^^-------------------------- an argument of type Box<(dyn for<'r, 's> FnOnce(&'r CreationContext<'s>) -> Box<(dyn App + 'static)> + 'static)> is missing
|
note: expected struct WebOptions, found struct Box
--> egui_node_graph_example\src\lib.rs:22:34
|
22 | eframe::start_web(canvas_id, Box::new(app))
| ^^^^^^^^^^^^^
= note: expected struct WebOptions
found struct Box<NodeGraphExample>
note: function defined here
--> C:\Users\2021.cargo\registry\src\github.com-1ecc6299db9ec823\eframe-0.19.0\src\lib.rs:112:8
|
112 | pub fn start_web(
| ^^^^^^^^^
help: provide the argument
|
22 | eframe::start_web(canvas_id, /* WebOptions /, / Box<(dyn for<'r, 's> FnOnce(&'r CreationContext<'s>) -> Box<(dyn App + 'static)> + 'static)> */)
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error[E0308]: mismatched types
--> egui_node_graph_example\src\lib.rs:22:5
|
20 | pub fn start(canvas_id: &str) -> Result<(), eframe::wasm_bindgen::JsValue> {
| ----------------------------------------- expected Result<(), JsValue> because of return type
21 | let app = NodeGraphExample::default();
22 | eframe::start_web(canvas_id, Box::new(app))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected (), found struct Arc
|
= note: expected enum Result<(), _>
found enum Result<Arc<eframe::egui::mutex::Mutex<AppRunner>>, _>

Some errors have detailed explanations: E0061, E0308.
For more information about an error, try rustc --explain E0061.
error: could not compile egui_node_graph_example due to 2 previous errors

Missing egui serde support?

Hey, been looking at the "persistence" feature. It seems to be missing the "egui/serde" feature? Seems to affect egui::Pos2 and egui::Vec2 uses

Will blackjack use egui_node_graph ?

The reason I am asking : After playing and extending a bit the provided egui_node_graph_example I started to dive into the next stage: Adapting a minimal version of the blackjack PolyAsm based graph compiler, so I can do node traversal and finally get some executable instructions. Unfortunately this is a bit outside my comfort zone and I ran into an error about wrong lifetimes and at several places I was not comfortable what I was doing to get things to compile and I was also in doubt how to adapt blackjack NodeData to egui_node_graph Node<NodeData>.user_data.

Anyway, if you plan to make blackjack use egui_node_graph, then I probably could figure out myself how to get my minimal compiler example to work, otherwise a minimal working example of something like a graph compiler would be great.

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.