Giter Club home page Giter Club logo

svg-to-compose's Introduction

SVG to Compose

A command-line tool to convert SVG or an Android Vector Drawable (AVG) to Android Jetpack Compose Icons.

Table of content

Why?

We usually use Android Vector drawables to display icons on Android apps.

With the addition of Jetpack Compose, we don't use XML to write views, and we can use all the power of Kotlin to speed up the view development process.

With that in mind, what if we could also avoid using our old approach to import icons to our project, and now rely on ImageVectors and creating the icons using Kotlin, following the same approach made for all Material Icons, from Google.

There are existing plugins available, but they usually don't optimize the svg before converting it to Jetpack Compose Icons.

This command-line tool adds that functionality.

Platform support

Platform With optimization Without optimization
macOS Arm64
macOS x64
Linux x64
Windows (mingwX64)
Windows (WSL) ❌ (not tested)

Installation

This CLI tool relies on Kotlin Native to parse the SVG/AVD file, thus we need the binaries to be able to run it. There are two ways to achieve this:

  1. Download the s2c script from this repository and save it in your preferred folder, or
  2. Cloning the project.

The script will take care of downloading or building the native binaries.

After downloading the script or cloning the project:

  1. Give execution permission to the script:
chmod +xw s2c
  1. If you want to run the script from anywhere, you might need to add it to your path, in your ~/.bashrc, ~/.zshrc, ~/.zshenv, or ~/.profile:
export PATH=<s2c path>:$PATH

Replacing <s2c path> to the folder's path where you stored the script

External Dependencies

This script relies on three others to perform the optimization:

  • SVGO: Optimizes the SVG reducing the paths.
npm -g install svgo
  • Avocado: Optimizes Android VectorDrawable and AnimatedVectorDrawable xml files.
npm -g install avocado

Important

If you don't want to optimize the SVG before converting it, you can just disable the optimization using the parameter -opt or --optimize passing false.

Optimization is enabled by default.

Using the command-line tool

Help for advance usage:

s2c --help

Help output:

Usage: client [<options>] <path>

Options:
  -v, --version                     Show this CLI version
  -p, --package=<text>              Specify icons' package. This will replace package at the top of the icon file
  -t, --theme=<text>                Specify project's theme name. This will take place in the Icon Preview composable function and in the ImageVector Builder's names.
  -o, --output=<text>               output filename; if no .kt extension specified, it will be automatically added. In case of the input is a directory, output MUST also be a directory.
  -opt, --optimize=true|false       Enable SVG/AVG optimization before parsing to Jetpack Compose icon. The optimization process uses the following programs: svgo, avocado from NPM Registry
  -rt, --receiver-type=<text>       Adds a receiver type to the Icon definition. This will generate the Icon as a extension of the passed argument.

                                    E.g.: s2c <args> -o MyIcon.kt -rt Icons.Filled my-icon.svg will creates the Compose Icon:

                                    val Icons.Filled.MyIcon: ImageVector.
  --add-to-material                 Add the icon to the Material Icons context provider.
  --debug                           Enable debug log.
  --verbose                         Enable verbose log.
  -np, --no-preview, --kmp          Removes the preview function from the file. It is very useful if you are generating the icons for KMP, since KMP doesn't support previews yet.
  --make-internal                   Mark the icon as internal
  --minified                        Remove all comments explaining the path logic creation and inline all method parameters.
  -r, --recursive                   Enables parsing of all files in the input directory, including those in subdirectories up to a maximum depth of 10
  --recursive-depth, --depth=<int>  The depth level for recursive file search within directory. The default value is 10.
  -h, --help                        Show this message and exit

Arguments:
  <path>  file *.svg | *.xml | directory

Convert an SVG to a Compose Icon:

s2c -o OutputIconFile.kt \
    -p your.app.package.icon \
    -t your.app.package.theme.YourAppComposeTheme \
    input.svg

Convert an Android Drawable Vector to a Compose Icon:

s2c -o OutputIconFile.kt \
  -p your.app.package.icon \
  -t your.app.package.theme.YourAppComposeTheme \
  input.xml

Convert all SVGs and Android Drawable Vectors within a directory to Compose Icons:

s2c -o /my/desired/directory \
  -p your.app.package.icon \
  -t your.app.package.theme.YourAppComposeTheme \
  /my/svg/or/xml/directory

Warning

If the input path is a directory and the output is not a directory, the CLI will not parse any icon and will finish the execution with an error.

Disabling SVG optimization:

s2c -o OutputIconFile.kt \
  -p your.app.package.icon \
  -t your.app.package.theme.YourAppComposeTheme \
  --opitmize false \
  input.svg

Important

If you don't specify the full qualifier of the Theme, you'll need to add the import it later.

Result Examples

Simple SVG file

Without optimization

Command:

./s2c -o <app path>/app/src/main/java/dev/tonholo/composeicons/ui/icon/ShieldSolid.kt \
      -p dev.tonholo.composeicons.ui.icon \
      --theme dev.tonholo.composeicons.ui.theme.ComposeIconsTheme \
      -opt=false \
      <parent-path>/shield-halved-solid.svg

Input file: shield-halved-solid.svg

Output file: ShieldSolid.nonoptimized.kt

With optimization

Command:

./s2c -o <app path>/app/src/main/java/dev/tonholo/composeicons/ui/icon/ShieldSolid.kt \
      -p dev.tonholo.composeicons.ui.icon \
      --theme dev.tonholo.composeicons.ui.theme.ComposeIconsTheme \
      -opt=true \
      <parent-path>/shield-halved-solid.svg

Input file: shield-halved-solid.svg

Output file: ShieldSolid.svg.optimized.kt

Complex SVG file

Without optimization

Command:

./s2c -o <app path>/app/src/main/java/dev/tonholo/composeicons/ui/icon/Illustration.kt \
      -p dev.tonholo.composeicons.ui.icon \
      --theme dev.tonholo.composeicons.ui.theme.ComposeIconsTheme \
      -opt=false \
      <parent-path>/illustration.svg

Input file: illustration.svg

Output file: Illustration.svg.nonoptimized.kt

With optimization

Command:

./s2c -o <app path>/app/src/main/java/dev/tonholo/composeicons/ui/icon/Illustration.kt \
      -p dev.tonholo.composeicons.ui.icon \
      --theme dev.tonholo.composeicons.ui.theme.ComposeIconsTheme \
      -opt=true \
      <parent-path>/illustration.svg

Input file: illustration.svg

Output file: Illustration.svg.optimized.kt

License

This software is released under the terms of the MIT license.

svg-to-compose's People

Contributors

rafaeltonholo avatar renovate[bot] 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

Watchers

 avatar

Forkers

remcotaal

svg-to-compose's Issues

Handle Gradient and Item Tags

Currently the script does not support icons with and .

Taking the default android ic_launcher_foreground as example:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:width="108dp"
    android:height="108dp"
    android:viewportWidth="108"
    android:viewportHeight="108">
    <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
        <aapt:attr name="android:fillColor">
            <gradient
                android:endX="85.84757"
                android:endY="92.4963"
                android:startX="42.9492"
                android:startY="49.59793"
                android:type="linear">
                <item
                    android:color="#44000000"
                    android:offset="0.0" />
                <item
                    android:color="#00000000"
                    android:offset="1.0" />
            </gradient>
        </aapt:attr>
    </path>
    <path
        android:fillColor="#FFFFFF"
        android:fillType="nonZero"
        android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
        android:strokeWidth="1"
        android:strokeColor="#00000000" />
</vector>

The gradient should be represented in the fill arg of the generated compose icon

Support other Path attributes

Right now the script only parses fillColor from the path xml.

Consider an icon like this British flag below:

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="300dp"
    android:height="300dp"
    android:viewportWidth="300"
    android:viewportHeight="300">
  <path
      android:pathData="M0,0h300v300h-300z"
      android:fillColor="#00247d"/>
  <path
      android:fillColor="#FF000000"
      android:pathData="M0,0L300,300M300,0L0,300"
      android:strokeWidth="60"
      android:strokeColor="#fff"/>
  <path
      android:fillColor="#FF000000"
      android:pathData="M0,0L300,300M300,0L0,300"
      android:strokeWidth="40"
      android:strokeColor="#cf142b"/>
  <path
      android:pathData="M150,150v-30L30,0H0zh30L300,30V0zv30L270,300H300zh-30L0,270V300z"
      android:fillColor="#fff"/>
  <path
      android:fillColor="#FF000000"
      android:pathData="M150,0V300M0,150H300"
      android:strokeWidth="100"
      android:strokeColor="#fff"/>
  <path
      android:fillColor="#FF000000"
      android:pathData="M150,0V300M0,150H300"
      android:strokeWidth="60"
      android:strokeColor="#cf142b"/>
</vector>

Without parsing strokeWidth, strokeColor the generated compose icon will not be accurate.
Full list of possible attributes are:

xml attr Compose equivalent
fillColor fill
fillAlpha fillAlpha
fillType pathFillType
strokeColor stroke
strokeAlpha strokeAlpha
strokeLineCap strokeLineCap
strokeLineJoin strokeLineJoin
strokeMiterLimit strokeMiterLimit
strokeWidth strokeLineWidth

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

This repository currently has no open or pending branches.

Detected dependencies

github-actions
.github/actions/setup-nodejs/action.yml
  • actions/cache v4
  • actions/setup-node v4
.github/actions/setup/action.yml
  • actions/setup-java v4
  • gradle/gradle-build-action v3
.github/workflows/pull_request.yml
  • actions/checkout v4
  • actions/checkout v4
  • actions/cache v4
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
.github/workflows/release.v2.yml
  • actions/checkout v4
  • actions/cache v4
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/download-artifact v4
  • actions/download-artifact v4
  • softprops/action-gh-release v2
.github/workflows/release.yml
  • actions/checkout v4
  • actions/cache v4
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
  • burrunan/gradle-cache-action v1
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/upload-artifact v4
  • actions/download-artifact v4
  • actions/download-artifact v4
gradle
gradle.properties
settings.gradle.kts
build.gradle.kts
gradle/libs.versions.toml
  • com.github.ajalt.clikt:clikt 4.4.0
  • com.squareup.okio:okio 3.9.0
  • io.gitlab.arturbosch.detekt:detekt-formatting 1.23.6
  • com.codingfeline.buildkonfig:buildkonfig-gradle-plugin 0.15.1
  • com.fleeksoft.ksoup:ksoup 0.1.2
  • org.jetbrains.kotlin.multiplatform 2.0.0
  • org.jetbrains.kotlin.plugin.serialization 2.0.0
  • io.gitlab.arturbosch.detekt 1.23.6
sample-app/build.gradle.kts
sample-app/gradle/libs.versions.toml
  • androidx.activity:activity-compose 1.9.0
  • androidx.compose:compose-bom 2024.05.00
  • androidx.core:core-ktx 1.13.1
  • com.google.android.material:material 1.12.0
  • androidx.lifecycle:lifecycle-runtime-ktx 2.8.1
  • com.android.application 8.4.1
  • org.jetbrains.kotlin.android 2.0.0
  • org.jetbrains.kotlin.plugin.compose 2.0.0
svg-to-compose/build.gradle.kts
gradle-wrapper
gradle/wrapper/gradle-wrapper.properties
  • gradle 8.8

  • Check this box to trigger a request for Renovate to run again on this repository

Use PathParser instead of building paths in the code

The converter produces code like this (fragment taken from one of the samples)

 // M256 0 c4.6 0 9.2 1 13.4 2.9 l188.3 79.9 c22 9.3 38.4 31 38.3 57.2 -0.5 99.2 -41.3 280.7 -213.6 363.2 -16.7 8 -36.1 8 -52.8 0 C57.3 420.7 16.5 239.2 16 140 c-0.1 -26.2 16.3 -47.9 38.3 -57.2 L242.7 2.9 C246.8 1 251.4 0 256 0z m0 66.8 v378 C394 378 431.1 230.1 432 141.4 L256 66.8z
path(
    fill = SolidColor(Color(0xFF1E3050)),
) {
    // M 256 0
    moveTo(x = 256.0f, y = 0.0f)
    // c 4.6 0 9.2 1 13.4 2.9
    curveToRelative(
        dx1 = 4.6f,
        dy1 = 0.0f,
        dx2 = 9.2f,
        dy2 = 1.0f,
        dx3 = 13.4f,
        dy3 = 2.9f,
    )
    // other instructions here
}

Creating a path in code makes it quite verbose to the point where the IDE becomes unresponsive for larger images.

You could consider using a PathParser instead to produce something like this instead

addPath(
  PathParser().parsePathString(
    "M256 0 c4.6 0 9.2 1 13.4 2.9 l188.3 79.9 c22 9.3 38.4 31 38.3 57.2 -0.5 99.2 -41.3 280.7 -213.6 363.2 -16.7 8 -36.1 8 -52.8 0 C57.3 420.7 16.5 239.2 16 140 c-0.1 -26.2 16.3 -47.9 38.3 -57.2 L242.7 2.9 C246.8 1 251.4 0 256 0z m0 66.8 v378 C394 378 431.1 230.1 432 141.4 L256 66.8z"
  ).toNodes()
)

The result would be much shorter.

Consider when the fill-rule is even-odd in the generated code

For an svg like this

<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="Icon/24/Attention-Filled">
<path id="Subtract" fill-rule="evenodd" clip-rule="evenodd" d="M21.5 12C21.5 17.2467 17.2467 21.5 12 21.5C6.75329 21.5 2.5 17.2467 2.5 12C2.5 6.75329 6.75329 2.5 12 2.5C17.2467 2.5 21.5 6.75329 21.5 12ZM12 7.25C12.4142 7.25 12.75 7.58579 12.75 8V13C12.75 13.4142 12.4142 13.75 12 13.75C11.5858 13.75 11.25 13.4142 11.25 13V8C11.25 7.58579 11.5858 7.25 12 7.25ZM12 17C12.5523 17 13 16.5523 13 16C13 15.4477 12.5523 15 12 15C11.4477 15 11 15.4477 11 16C11 16.5523 11.4477 17 12 17Z" fill="#121212"/>
</g>
</svg>

Which shows a circle with a exclamation mark in the middle, and should be rendered like this
image

When using this tool (with optimization = false) to generate a composable I get this

val AttentionFilled: ImageVector
    get() {
        val current = _attentionFilled
        if (current != null) return current

        return ImageVector.Builder(
            name = "AttentionFilled",
            defaultWidth = 24.0.dp,
            defaultHeight = 24.0.dp,
            viewportWidth = 24.0f,
            viewportHeight = 24.0f,
        ).apply {
            group {
                // M21.5 12 C21.5 17.2467 17.2467 21.5 12 21.5 C6.75329 21.5 2.5 17.2467 2.5 12 C2.5 6.75329 6.75329 2.5 12 2.5 C17.2467 2.5 21.5 6.75329 21.5 12Z M12 7.25 C12.4142 7.25 12.75 7.58579 12.75 8 V13 C12.75 13.4142 12.4142 13.75 12 13.75 C11.5858 13.75 11.25 13.4142 11.25 13 V8 C11.25 7.58579 11.5858 7.25 12 7.25Z M12 17 C12.5523 17 13 16.5523 13 16 C13 15.4477 12.5523 15 12 15 C11.4477 15 11 15.4477 11 16 C11 16.5523 11.4477 17 12 17Z
                path(
                  fill = SolidColor(Color(0xFF121212)),
                ) {
                    // M 21.5 12
                    moveTo(x = 21.5f, y = 12.0f)
                    // C 21.5 17.2467 17.2467 21.5 12 21.5
                    curveTo(
                        x1 = 21.5f,
                        y1 = 17.2467f,
                        x2 = 17.2467f,
                        y2 = 21.5f,
                        x3 = 12.0f,
                        y3 = 21.5f,
                    )
                    // C 6.75329 21.5 2.5 17.2467 2.5 12
                    curveTo(
                        x1 = 6.75329f,
                        y1 = 21.5f,
                        x2 = 2.5f,
                        y2 = 17.2467f,
                        x3 = 2.5f,
                        y3 = 12.0f,
                    )
                    // C 2.5 6.75329 6.75329 2.5 12 2.5
                    curveTo(
                        x1 = 2.5f,
                        y1 = 6.75329f,
                        x2 = 6.75329f,
                        y2 = 2.5f,
                        x3 = 12.0f,
                        y3 = 2.5f,
                    )
                    // C 17.2467 2.5 21.5 6.75329 21.5 12z
                    curveTo(
                        x1 = 17.2467f,
                        y1 = 2.5f,
                        x2 = 21.5f,
                        y2 = 6.75329f,
                        x3 = 21.5f,
                        y3 = 12.0f,
                    )
                    close()
                    // M 12 7.25
                    moveTo(x = 12.0f, y = 7.25f)
                    // C 12.4142 7.25 12.75 7.58579 12.75 8
                    curveTo(
                        x1 = 12.4142f,
                        y1 = 7.25f,
                        x2 = 12.75f,
                        y2 = 7.58579f,
                        x3 = 12.75f,
                        y3 = 8.0f,
                    )
                    // V 13
                    verticalLineTo(y = 13.0f)
                    // C 12.75 13.4142 12.4142 13.75 12 13.75
                    curveTo(
                        x1 = 12.75f,
                        y1 = 13.4142f,
                        x2 = 12.4142f,
                        y2 = 13.75f,
                        x3 = 12.0f,
                        y3 = 13.75f,
                    )
                    // C 11.5858 13.75 11.25 13.4142 11.25 13
                    curveTo(
                        x1 = 11.5858f,
                        y1 = 13.75f,
                        x2 = 11.25f,
                        y2 = 13.4142f,
                        x3 = 11.25f,
                        y3 = 13.0f,
                    )
                    // V 8
                    verticalLineTo(y = 8.0f)
                    // C 11.25 7.58579 11.5858 7.25 12 7.25z
                    curveTo(
                        x1 = 11.25f,
                        y1 = 7.58579f,
                        x2 = 11.5858f,
                        y2 = 7.25f,
                        x3 = 12.0f,
                        y3 = 7.25f,
                    )
                    close()
                    // M 12 17
                    moveTo(x = 12.0f, y = 17.0f)
                    // C 12.5523 17 13 16.5523 13 16
                    curveTo(
                        x1 = 12.5523f,
                        y1 = 17.0f,
                        x2 = 13.0f,
                        y2 = 16.5523f,
                        x3 = 13.0f,
                        y3 = 16.0f,
                    )
                    // C 13 15.4477 12.5523 15 12 15
                    curveTo(
                        x1 = 13.0f,
                        y1 = 15.4477f,
                        x2 = 12.5523f,
                        y2 = 15.0f,
                        x3 = 12.0f,
                        y3 = 15.0f,
                    )
                    // C 11.4477 15 11 15.4477 11 16
                    curveTo(
                        x1 = 11.4477f,
                        y1 = 15.0f,
                        x2 = 11.0f,
                        y2 = 15.4477f,
                        x3 = 11.0f,
                        y3 = 16.0f,
                    )
                    // C 11 16.5523 11.4477 17 12 17z
                    curveTo(
                        x1 = 11.0f,
                        y1 = 16.5523f,
                        x2 = 11.4477f,
                        y2 = 17.0f,
                        x3 = 12.0f,
                        y3 = 17.0f,
                    )
                    close()
                }
            }
        }.build().also { _attentionFilled = it }
    }

However this renders this:
image

Going in and adding this line pathFillType = PathFillType.EvenOdd, to the path() makes this work again, but I had to do it manually here.

Note that when I use the optimized flag then it is not fixed even with changing this pathFillType = PathFillType.EvenOdd, which I suppose happens as the optimization does not take this into consideration so it breaks the svg in unexpected ways.

I am not sure exactly which part it is that is failing here. Is the fill type not read from the svg file, or is our svg file somehow not compatible with what this tool can do, or what else could I do to encounter the correct behavior correctly?

Perhaps I could just run all the icons that it imports wrong though the non-optimized script and then go in an manually try to match the fill types as a workaround for now, but that can be quite some work.

Fails silently when encountering files with spaces in them

Thanks for this project, it's a great workflow optimization!

When processing a dir that contains svg names with spaces in them, the script will process files without spaces until it encounters the files with the space.
When closely inspecting the logs, you'll find out; but the script still completes successfully, even though it stopped processing with an error (remaining files without spaces are not processed)

./s2c.sh \
 -o ./ \
 -p your.app.package.icon \
 -t your.app.package.theme.YourAppComposeTheme \
 -cp YourAppIcons \
 ./Arrow\ Left.svg
./s2c.sh: line 1001: [: ./Arrow: binary operator expected
🔍 Directory detected
ls: ./Arrow: No such file or directory
ls: Left.svg: No such file or directory

🎉 SVG parsed to Jetpack Compose icon with success 🎉

Expected: either support files with spaces by sanitizing the name (just remove the whitespaces) or exit with an error status

Reuse elements from defs

I seems that currently elements declared in SVGs <defs> (e.g. those in brasil.svg) are inlined. The script could create a Composable function for each reusable element and call it with appropriate parameters instead of repeatedly creating the paths. It would reduce the generated Composable function size and complexity.

EPIC: Improve Precision in Geometric Calculations

The current implementation of the CLI tool, which parses SVG files to Compose Icons, is encountering issues with precision in geometric calculations. This precision loss affects the accuracy of the transformations applied to the SVG elements, resulting in visual discrepancies in the generated icons.

Currently, we are parsing Float to Double while calculating and back to Float, which could be avoided if we directly use Double.

Components to be migrated:

  • PathTransformation
  • ArcTransformation
  • Point2D (also merge PrecisePoint2D after migrating Point2D to use Double)
  • AffineTransformation
  • PathNodes

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.