Giter Site home page Giter Site logo

slint-ui / slint Goto Github PK

View Code? Open in Web Editor NEW
15.1K 92.0 492.0 32.11 MB

Slint is a declarative GUI toolkit to build native user interfaces for Rust, C++, or JavaScript apps.

Home Page: https://slint.dev

License: Other

Rust 72.07% C++ 4.28% JavaScript 0.33% CMake 0.50% HTML 0.03% Python 0.53% TypeScript 3.46% Shell 0.09% CSS 0.11% C 0.01% Objective-C 0.55% Java 0.26% Slint 17.78%
rust embedded-devices language gui wasm cpp javascript ui webassembly declarative-ui

slint's Introduction

Slint Slint

Build Status REUSE status Discussions

Slint is a declarative GUI toolkit to build native user interfaces for desktop and embedded applications written in Rust, C++, or JavaScript. The name Slint is derived from our design goals:

  • Scalable: Slint should support responsive UI design, allow cross-platform usage across operating systems and processor architectures and support multiple programming languages.
  • Lightweight: Slint should require minimal resources, in terms of memory and processing power, and yet deliver a smooth, smartphone-like user experience on any device.
  • Intuitive: Designers and developers should feel productive while enjoying the GUI design and development process. The design creation tools should be intuitive to use for the designers. Similarly for the developers, the APIs should be consistent and easy to use, no matter which programming language they choose.
  • Native: GUI built with Slint should match the end users' expectations of a native application irrespective of the platform - desktop, mobile, web or embedded system. The UI design should be compiled to machine code and provide flexibility that only a native application can offer: Access full operating system APIs, utilize all CPU and GPU cores, connect to any peripheral.

We invite you to use Slint and be part of its community.

Visit #MadeWithSlint to view some of the projects using Slint and join us in the Slint community.

Current Status

Slint is in active development. The state of support for each platform is as follows:

  • Embedded: Ready. Slint is being used by customers in production on embedded devices running embedded Linux and Windows. The Slint run-time requires less than 300KiB of RAM and can run on different processor architectures such as ARM Cortex M, ESP32, STM32 from the MCU category to ARM Cortex A, Intel x86 from the MPU category.
  • Desktop: In Progress. While Slint is a good fit on Windows, Linux and Mac, we are working on improving the platform support in subsequent releases.
  • Web: In Progress. Slint apps can be compiled to WebAssembly and can run in a web browser. As there are many other web frameworks, the web platform is not one of our primary target platforms. The web support is currently limited to demo purposes.
  • Mobile
    • Android: In Progress. Track the progress of work here #46.
    • iOS: Todo. Support for iOS will commence after the initial support for Android is completed.

Accessibility

Slint supports keyboard based navigation of many widgets, and user interfaces are scalable. The basic infrastructure for assistive technology like screen readers is in place. We're aware that more work is needed to get best-of-class support for users with special needs.

Demos

Embedded

RaspberryPi STM32 RP2040
Video of Slint on Raspberry Pi Video of Slint on STM32 Video of Slint on RP2040

Desktop

Windows macOS Linux
Screenshot of the Gallery on Windows Screenshot of the Gallery on macOS Screenshot of the Gallery on Linux

Web using WebAssembly

Printer Demo Slide Puzzle Energy Monitor Widget Gallery
Screenshot of the Printer Demo Screenshot of the Slide Puzzle Screenshot of the Energy Monitor Demo Screenshot of the Gallery Demo

Get Started

Hello World

The UI is defined in a Domain Specific Language that is declarative, easy to use, intuitive, and provides a powerful way to describe graphical elements, their placement, their hierarchy, property bindings, and the flow of data through the different states.

Here's the obligatory "Hello World":

export component HelloWorld inherits Window {
    width: 400px;
    height: 400px;

    Text {
       y: parent.width / 2;
       x: parent.x + 200px;
       text: "Hello, world";
       color: blue;
    }
}

Documentation

For more details, check out the Slint Language Documentation.

The examples folder contains examples and demos, showing how to use the Slint markup language and how to interact with a Slint user interface from supported programming languages.

The docs folder contains a lot more information, including build instructions, and internal developer docs.

Refer to the README of each language directory in the api folder:

Architecture

An application is composed of the business logic written in Rust, C++, or JavaScript and the .slint user interface design markup, which is compiled to native code.

Architecture Overview

Compiler

The .slint files are compiled ahead of time. The expressions in the .slint are pure functions that the compiler can optimize. For example, the compiler could choose to "inline" properties and remove those that are constant or unchanged. In the future we hope to improve rendering time on low end devices by pre-processing images and text. The compiler could determine that a Text or an Image element is always on top of another Image in the same location. Consequently both elements could be rendered ahead of time into a single element, thus cutting down on rendering time.

The compiler uses the typical compiler phases of lexing, parsing, optimization, and finally code generation. It provides different back-ends for code generation in the target language. The C++ code generator produces a C++ header file, the Rust generator produces Rust code, and so on. An interpreter for dynamic languages is also included.

Runtime

The runtime library consists of an engine that supports properties declared in the .slint language. Components with their elements, items, and properties are laid out in a single memory region, to reduce memory allocations.

Rendering backends and styles are configurable at compile time:

  • The femtovg renderer uses OpenGL ES 2.0 for rendering.
  • The skia renderer uses Skia for rendering.
  • The software renderer uses the CPU with no additional dependencies.

NOTE: When Qt is installed on the system, the qt style becomes available, using Qt's QStyle to achieve native looking widgets.

Tooling

We have a few tools to help with the development of .slint files:

  • A LSP Server that adds features like auto-complete and live preview of the .slint files to many editors.
  • It is bundled in a Visual Studio Code Extension available from the market place.
  • A slint-viewer tool which displays the .slint files. The --auto-reload argument makes it easy to preview your UI while you are working on it (when using the LSP preview is not possible).
  • SlintPad, an online editor to try out .slint syntax without installing anything (sources).
  • An updater to convert the .slint files from previous versions to newer versions.
  • An experimental Figma importer.

Please check our Editors README for tips on how to configure your favorite editor to work well with Slint.

License

You can use Slint under any of the following licenses, at your choice:

  1. Royalty-free license,
  2. GNU GPLv3,
  3. Paid license.

See also the Licensing FAQ

Contributions

We welcome your contributions: in the form of code, bug reports or feedback.

Frequently Asked Questions

Please see our separate FAQ.

About us (SixtyFPS GmbH)

We are passionate about software - API design, cross-platform software development and user interface components. Our aim is to make developing user interfaces fun for everyone: from JavaScript, C++, or Rust developers all the way to UI/UX designers. We believe that software grows organically and keeping it open source is the best way to sustain that growth. Our team members are located remotely in Germany.

Stay up to date

Contact us

Feel free to join Github discussions for general chat or questions. Use Github issues to report public suggestions or bugs.

We chat in our Mattermost instance where you are welcome to listen in or ask your questions.

You can of course also contact us privately via email to [email protected].

slint's People

Contributors

a1ecbr0wn avatar aurindam avatar aursen avatar be-ing avatar chrischinchilla avatar colelawrence avatar darknight avatar fieran100 avatar flovangh avatar flukejones avatar guiguiprim avatar hunger avatar jamesblacklock avatar jpnurmi avatar jrmoulton avatar jturcotte avatar levrik avatar lukas-jung avatar mikom avatar montel avatar notfirefox avatar nununoisy avatar ogoffart avatar patrickelectric avatar ryban avatar rybertm avatar task-jp avatar tronical avatar waqar144 avatar zajozor 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  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

slint's Issues

Add support for css named colors in the compiler

Allow for the convenient use of the common color names in .60 markup.

We could use https://crates.io/crates/css-color-parser2

The documentation of the language reference should also mention what syntax is possible.

You can use plain names if it's evident to the compiler that the property type is a color. If that's not clear, then a Color. prefix is required. The hashed variant is always a color. This permits the following two variants:

Rectangle {
    color: rgba(00, ff, 00, ff);
}

or

Rectangle {
    signal hsl;
    color: Color.hsl(12, 34, 56);
}

Add basic language server support

This ticket tracks the infrastructure for the language server support and thus IDE integration.

Proposed acceptance criteria:
* The build produces a binary that supports the minimal language server protocol.
* The existing vs code extension template is extended to include launching the LSP binary for .60 files.
* The server tries to compile the edited .60 markup code and reports errors and warnings.

Error recovery

  • In the parser: once there is a parsing error (because of a failing except), further parsing error for that node should be silenced.
  • If there is a parse error, we should still be able to build an object tree. The problem is that right now we panic if the expected nodes are missing, but we should take that into account and have empty or error nodes in our object_tree and expression_tree

Having proper expression_tree even in case of error is going to be important if we want to do refactoring on possibly incomplete code.

Basic text input

This ticket tracks the feature of being able to enter plain text (no formatting) using a keyboard.

Acceptance criteria: It's possible to enter text using a simple single-line text input field. A cursor is visible and can be placed with the keyboard and mouse. Text can be selected with the mouse.

Depends on #55

Make C++ packages available via Conan

This ticket tracks the plan of making it easier to use the C++ API of SixtyFPS by making it available via the Conan C++ package manager.

Acceptance Criteria: Documentation that explains how to use Conan to easily use the existing CMake integration in C++ Applications. This may imply the availability of binary packages, but source packages would also be acceptable.

Accessibility: support for screen readers

At the risk of this being a very open task, this ticket tracks the foundation of accessibility support. This is by no means everything, but it's a start.

Acceptance Criteria: It's possible to navigate the widget gallery demo using voiceover and a keyboard on at least one platform (such as macOS or Linux). This means the text of labels, etc. is read, it's possible to use the keyboard to navigate between different elements and activate them.

Keyboard focus

For accessibility reasons, the UI need to be usable with the keyboard.
So we need a keyboard focus. (also for text input)

Element that can gain the focus should be known to the compiler, and would have a has_focus property which specify if the element currently has the focus.

  • Do we need a difference between active_focus and focus ? (i.e: is it possible that a element has the focus property set to true while not in fact, having the current focus because, say, another component is active in another "tab" or "window")

We would also need a way to tell where the focus should go by pressing tab. By default, one would compute the most reasonable chain geometrically. but this should be possible to override in the .60 using tab index property, or some kind of chaining

QPainter-based backend

This ticket tracks the idea to render the graphics primitives using QPainter to a QImage and then somehow into the Window.

This would allow integrating the existing "Qt style" in a more seamless and efficient way.

Acceptance Criteria: Rendering primitives are rendered using QPainter.

Add support for translations

This ticket tracks the ability to translate a Slint user interface in a way that allows annotating translatable strings,

  • having the infrastructure and documentation for extracting said strings from .slint
  • using a third-party tool to translate them and feed the translations back into the Slint build process.
  • Finally the translation that's chosen at compile time should be embedded in the resulting compiled code and be visible at run-time.
  • This may include support for multiple languages and choosing between them at runtime (Language can be changed at runtime)

Proposal.

  • Use a @tr(...) macro with the same syntax as the rust tr! macro from the tr crate. (cf woboq/tr#1 )
  • update the xtr tool on crate.io to also support .slint file
  • Behind the scene, use gettext

Workaround until this is implemented.

Put all the string in a global object and do the translation in native code
See #33 (comment)

Change unit system regarding pixels

Currently the unit system is implemented like this:

  • px is a physical pixel, for example 100px.
  • lx is a logical pixel, for example 100. It is scaled to physical pixels according to the window's device pixel ratio.

This is inconsistent with CSS and we want to promote the use of scalable pixels.

We propose to change this:

  • px becomes the logical pixel that's scaled according to the DPR.
  • phx becomes the physical, unscaled pixel.

Add support for dashes in identifiers

This would permit more compatibility or similarity with CSS and allow for properties like font-size, padding-top, etc.

We should normalise such names internally to use underscores (type-name -> type_name). Such a change has additional implications on the name lookup: If a name that's being looked up appears to be in normalised form (contains underscore) and we find it, then we should continue a lookup for the name with underscores replaced by dashes and produce an error message if we find that one, too.

In the generate code we should use the normalised form.

Styling

We want the ability to

  • Change the style globably for all widgets
  • Make it easy to write your own style which is applied globaly
  • Change the style of particular widgets

String iterpolation

We want to be able to easily format strings with other expression.
Possible syntax:

  1. Same as javascript
 text:  `The window with is ${ root.width / 1px } and its title is ${ root.title }` ;

Advantage is that people coming from JS will be familiar with this.

  1. Using normal quote and use \{
text: "The window with is \{ root.width / 1px } and its title is \{ root.title }";

The advantage is that there is only one form of string. (I think JS uses backtick because using normal quote would not have been backward compatible, but we don't have this problem)


The question is how good does it play with translation?

Imagine we support this syntax:

text: tr!"The window with is \{ root.width / 1px } and its title is \{ root.title }";

The translation extracting tool could extract the string "The window with is {0} and its title is {1}" as the string to be translated.

Make it possible to define constants

I would like the ability to share constants or perhaps properties across different components, such as settings like common colours.

One such way would be to have a const keyword in a .60 file, which is part of the regular lookup process but results in those values being inlined:

export const <color> DefaultActiveColor : black;
const <string> DefaultName = "ok";
const <length> DefaultSize = 24lx;

export Button := Rectangle {
    const <string> foo : "hello"
    color: DefaultActiveColor;
}
import * as Colors from "theme_colors.60";

Rectangle {
    color: Colors.DefaultActiveColor;
}

Acceptance Criteria: A way of sharing common constants across different components without duplicating them. This also needs to be documented in the language reference.

`debug()` statement: show filename and line number

Commit 9fbb40d introduced a debug statement, but it still full of FIXME

The function arguments need to be handled.
The backend need to be chosen.

ideally, debug("hello", 3+3); should print

foobar.slint:42: hello 6

But the backend should be selected. There could be a runtime function that actually do the print, and the generated code could call it.

Improve testing ergonomics within `tests/cases`

Currently the tests in tests/cases have to repeat the code between the three language rust , cpp, js and that is fastidious as we cannot easily copy_paste.
We should make the tests more alike by adding some assert_eq function to C++

We should have a more generic test pseudo language that allow to do operations, maybe in 60

```generic
instance.property_foo = 42
assert_eq(instance.property_foo,  42);
```

Slider not releasing mouse control in Widget Gallery Demo

When releasing the mouse button outside the active area (panel delimited by black border), it's still possible to move the slider when moving the mouse.

Steps to reproduce:

  • left click on the slider
  • move mouse outside the active area
  • release mouse button
  • go back inside the active area
  • slider is still following the mouse

Add support for clipping items

This ticket tracks the ability of items to clip their contents and the content of their children to either the item's boundaries or a specified geometry.

More ergonomic unit handling

We should consider:

  • Allow 0 literal without unit to be converted to any unit.
  • Right now, someething like parent.width * img.width / img.height is not allowed because it is interpreted as (parent.width * img.width) / img.height which multiplies two lenght which is not allowed. While parent.width * (img.width / img.height) works fine. So we should allow higher dimensions, at least in partial results.

Global CMake build that builds everything

It would be great to have a top-level CMake build that builds the run-time library, the compiler, etc. everything needed in one go. That would make it much easier for C++ developers to use.

Add support for property aliases

Sometimes we have components that wrap others, such as LineEdit maybe using a TextInput internally. In that case we want to expose the TextEdit's text property in LineEdit, so that setting it forwards and reading it will fetch from the internal TextInput.

The ability to alias properties would help accomplish that.

Acceptance criteria: Aliases implemented, tested and documented.

Add support for transitions

This ticket tracks the ability to extend the existing support for states with transitions to be applied to properties when states are entered/exited.

Acceptance Criteria: Basic transitions can be used in .60 files, the syntax is documented and tested.

Add support for de-inlining in the compiler

Currently the compiler inlines every usage of a component, which will resulting in the duplication of binding code and increases the size of the binary.

This ticket tracks the ability of the compiler to avoid this inlining step, based on heuristics or explicit annotations.

Better syntax for properties and callback declaration.

Currently we have

property <int> my_property: 42;
callback foo; // no way to declare arguments

Proposed:

my_property := property(int) : 42;  
// callback with no parameters
my_signal := callback;
// callback with parameters
my_signal2 := callback(int, string);
// callback with two parameter and a handler
my_signal3 := callback(xx: int, yy: string) => { 
     use(xx);
     use(yy);
}

// handler for callback
my_signal => { /* ... */ }
my_signal2(aa, bb) => { /* ... */ }

Rationale is that := is used to declare new names for elements already, so we can re-use it for signals / properties.

Don't pollute target/<profile>/ directory with includes

A regular rust build will create header files in target/debug/include, for example. That's fine with sixtyfps, but for users it's not fine - it means every user will have that in their target/ directories.

We need to find a different location that we can find again, or somehow do this only when running within sixtyfps.

Add support for window icon

This ticket tracks the ability to set/get properties of window items that are rendered using the windowing system, such as the title or an icon image.

A list of possible properties:

  • title
  • icon
  • size constraints (maximum size, fixed size, etc.)

Optimize unused property

The compiler can detect that a property is not used and not create it so it does not take memory at runtime

Upload packages to crates.io

This ticket tracks the plan to make the Rust crates available on crates.io for easy use by everyone.

Acceptance Criteria: It's possible to use the Rust API via crates.io.

Add support for popup menus and menubar

This ticket tracks the ability to declare popup menus in .60 markup as well as the ability to integrate them into a menu bar.

This needs further refinement/triaging regarding a more actionable acceptance criteria.

Threading

Right now, everything happens in the same thread. The event loop is running and all the binding are run in the same thread. The user code (in C++ or rust) is also expected to run in the main thread.

We would like to consider running the sixtyfps eventloop in another thread.

We need to make sure that we do not have partial update. (if the user calls instance.set_x(42); instance.set_y(42); we do not want the rendering engine to render a frame with x set to the new value, and y to the previous value)

The user code should be able to control where its code is run (bindings evaluation, signal handler) by default, everything should happen in the user's thread.

Implementation wise, we would require to lock the component (or the window, or globally?) before doing property update, and unlock it when finish (think of it as begin_transation, end_transaction) We could automatically lock from the property setters, and unlock from the event loop.

Example of code (in rust but we could imagine something similar in C++)

let instance = MyWindow::new();
instance.on_some_signal(|| {
     // always called form this thread
     do_some_computation();
     instance.set_property_x(42);  // locks the rendering thread
     instance.set_property_y(42);  // lock is already locked in this thread, so does not blocks
     // the lock will be released automatically when the event loop executes.
})
instance.show(); // returns immediatly
// sixtyfps::run(); // would run the event loop and blocks
while sixtyfps::next_frame().await {} // delegate the event loop to an async executor

There would be two kind of binding: main-thread binding, and render-thread binding. The sixtyfps compiler can decide which binding can be run in the render thread, and which binding need to run in the main thread.

The lock() operation would look like this (atomic pseudo code)

while locked_thread != this_thread { 
     if locked_thread == nil {  locked_thread = this_thread; return;  }
     wait_condition.wait(); // blocks
     if event_to_process { process_the_events() }
}

Initial Android Support

This project is funded through NGIO Core via NLNet - https://nlnet.nl/project/SlintAndroid/

Here's a possible breakdown of tasks that we devised. We estimate this in the range of ~4 months of work.

  1. Base Android Port Milestone. This milestone covers the foundation of the port, to allow for the cross-compilation of a Rust based Slint application, deployment to a device, and showing primitive graphics on the screen, including text labels. Rendering is to be done with Skia, for GPU acceleration and native looking text rendering.
  1. Text Input Integration Milestone. This milestone covers the work to allow the user to interact with text input fields in Slint in a way that is consistent with other Android applications.
  • Show or hide the Android system virtual keyboard depending on focus on text input fields.
  • Ensure visibility of text input fields when focused and the virtual keyboard occupies a part of the screen. This includes scrolling and resizing the user interface.
  • Add support for text selection handles by rendering them at the correct location and allowing the user to interact with them way.
  1. Dialog and Menu Widgets. This milestone will add first-class support for Material Design Dialog and Menu components. Dialogs in general and Menus specifically are crucial for many application workflows on Android.
  • Add support for dialog components in the material design style and rendering them in a way that makes them appear like native dialogs.
  • Add support for declaring menu structures in the Slint language, rendering them with the material design style, and obtaining the user-selected menu item choice in native code.
  1. Date and Time Picker Widgets. This milestone will add support for Date and Time picker widgets, for use in dialogs. Both user interface elements represent complex controls and data models, due to different time zones and localization.
  • Add support for a widget in the material design style to allow the user to select a date, based on the system calendar and an optional provided input date.
  • Add support for a widget in the material design style for picking a time, based on the system time and an optional provided input time.

Update: Check the documentation to try the android backend: https://docs.rs/slint/latest/slint/android/index.html

Free GL resources when destroying items

Currently the rendering primitives stored in the rendering cache are not freed when an item gets destructed. This should be fixed by giving the CachedRenderingData a destructor called from all environments, add an Rc<> to the cache and notify the cache to free the entry.

Add support for FlexBox layout

Make it possible to use layouts in .60 files that are based on the CSS flex box layout.

Stretch offers itself as an implementation in Rust for that purpose.

Acceptance criteria: Flex layouts can be used in .60 files and their API is documented.

Name of special element ids (root, self)

There are currently three reserved element id:

  • root always refers to the component itself
  • self always refers to the element in which the binding is in
  • parent refers to the parent of self

We got feedback that root was confusing because it is ambiguous whether it refer to the application root (i.e, the Window) or to the element root.

Since we cannot give a custom name to the component root element, we need a keyword name to access it.
Potential suggestions would be to use self for the component root, and this for the element contained in the binding. or vice versa. or use component instead or root.

Online Editor

This ticket tracks the availability of support for a basic "online live editor" that allows editing .60 markup code in a text field in a web browser and update a separate canvas preview, for example by pressing an "Update Preview" button or by updating automatically.

This could be integrated into our documentation and allow users to explore examples further by playing around with them.

ListView

This ticket tracks the availability of an item that can be used to view a sub-set of a list of data coming from a model and that allows scrolling/panning.

The for syntax can be used to express the delegation of data to separate components that are instantiated depending on the visibility.

Proposed example syntax:

ContactListView := ListView {
    property<[ { name: string } ]> contacts;
    for data in contacts : MyContactDelegate {}
}


ContactListView {
    contacts: {
        [ {name: "Olivier"} ]
    }
}

Depends on #41

Acceptance Criteria: There exists a implemented, tested and documented ListView type. It can be implemented using a for statement and a flickable for 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.