Coder Social home page Coder Social logo

floooh / sokol Goto Github PK

View Code? Open in Web Editor NEW
6.5K 117.0 465.0 9.57 MB

minimal cross-platform standalone C headers

Home Page: https://floooh.github.io/sokol-html5

License: zlib License

C 83.67% Objective-C 13.23% Python 2.69% CMake 0.28% C++ 0.06% Shell 0.07% Batchfile 0.01%

sokol's Issues

vertex and uniform declaration issues

  • vertex layout definition should only happen in pipeline setup, not in shader setup (or only if required by backend API)
  • there should be 2 ways to define the vertex component location, which one is used depends on rendering backend and shader language dialect:
    • by name: sg_pipeline_setup_named_attr()
    • by slot index: sg_pipeline_setup_indexed_attr()
  • there must be several ways to describe uniform blocks in the shader setup:
    • per uniform, by name (traditional GL)
    • opaque uniform blocks (only useful with Metal, D3D11 or GL with shader-code-generation)
    • in general, shader setup should have different options depending on whether some sort of shader code generation is available, or shader setup needs to happen "manually"
  • vertex layout definition should have an explicit offset

sokol_entry

I started a library thats similar to https://github.com/jimon/entrypoint
The point of it:

  • Support: Windows, OSX, IOS, Android, X11
  • provide an entrypoint for your application
  • lifetime events
  • window/drawing surface & basic setup for Metal/OpenGL/DX11/WebGL
  • provide touch, gamepad, keyboard, mouse ect.
  • dragNdrop for OSX/Windows/X11

Other features I want to add but I'm not sure about them, or maybe add it after a 1.0:

  • async IO
  • network primitives (http requests ect.)
  • Android/iOS shop integration
  • iOS GameCenter
  • DX12 & Vulkan entrypoints

So the way its used its pretty similar to your sokol samples, in fact these entry points there is what I started with.
Currently I'm implementing OSX and iOS, both supporting Metal & OpenGL (for OpenGL it looks like I still need something like glad)

So this is how your clear-metal looks now with sokol_entry, not much changed, in fact for OSX/iOS its mostly renaming...

//------------------------------------------------------------------------------
//  clear-metal.c
//------------------------------------------------------------------------------
#include "sokol_gfx.h"
#include "sokol_entry/sokol_entry.h"

sg_pass_action pass_action;

void init(const sg_gfx_init_data *data) {
    sg_desc desc = {
        .mtl_device = data->mtl_device,
        .mtl_renderpass_descriptor_cb = data->mtl_renderpass_descriptor_cb,
        .mtl_drawable_cb = data->mtl_drawable_cb
    };
    sg_setup(&desc);
    pass_action = (sg_pass_action) {
        .colors[0] = { .action = SG_ACTION_CLEAR, .val = { 1.0f, 0.0f, 0.0f, 1.0f } }
    };
}
static bool wasFullscreen;
void frame() {
    sg_set_window_fullscreen(true);
    if (sg_is_window_fullscreen()) {
        wasFullscreen = true;
    }
    if (!sg_is_window_fullscreen() && wasFullscreen) {
        sg_quite();
    }
    
    /* animate clear colors */
    float g = pass_action.colors[0].val[1] + 0.01f;
    if (g > 1.0f) g = 0.0f;
    pass_action.colors[0].val[1] = g;

    /* draw one frame */
    sg_begin_default_pass(&pass_action, sg_get_window_width(), sg_get_window_width());
    sg_end_pass();
    sg_commit();
}

void shutdown() {
    sg_shutdown();
}

void sg_main() {
    sg_start(840, 480, 1, "Sokol Clear (Metal)", init, frame, shutdown);
}

So.. do you have interest in this? If not I will rename it to something else. Also any feedback is very welcome.

You can see my current WiP code here:
https://github.com/pjako/sokol/tree/entry/sokol_entry

image and render pass issues

  • a depth render buffer should be a normal image object, not included in another image
  • GL: the MSAA resolve render buffer should probably go into the render pass object

images should not be baked into pass object

The pass object should only have the minimally required information baked in (number of attachments, and their types - check Vulkan and D3D12 for exact requirements), and the actual render target image ids should be provided in the 'dynamic' sg_pass_action (probably would make sense to rename this struct then to something else).

Define uniforms with introspection.

Why are uniforms in sg_shader_uniform_block_desc set manually ?
Maybe the could be set automatically during the compilation of the shader with introspection ?

Thank you in advance.

Road to v1.0

  • validation for sg_update_buffer()
  • validation for sg_update_image()
  • WebGL2->WebGL fallback
  • 10-10-10-2 vertex format inconsistency (D3D11 only has unsigned format)
  • remove scissor_test_enabled, must be always enabled (Metal doesn't have that)
  • reset scissor rect to full render target size at start of pass
  • depth-bias stuff, need to check if there's a common ground for GL, D3D11, Metal
  • depth_clip_enabled(?) not supported by GLES2/WebGL or GLES3/WebGL2
  • antialiased_line_enabled(?) not supported on Metal, and with many caveats on D3D11
  • max_anisotropy (sampler state, so needs to go into sg_image_desc)
  • sampler state min_lod, max_lod, mip_lod_bias(?) mip_lod_bias as render state is only in D3D11, min_lod/max_lod is not in GLES2/WebGL (just ignore it there)
  • allow use of externally created "native" D3D11, GL and Metal buffer and image resources
  • Metal samples for iOS

OPTIONAL:

  • RaspberryPi2 samples

Framebuffer Ping-Ponging

Hello,

A common way to implement the gaussian blur in OpenGL/GLSL is to setup ping-ponging framebuffers and apply horizontal and vertical blurs with fixed width kernel repeatedly (ref).

Attempting to implement such setup in sokol seems to be quite tricky. While it is possible to change the input texture by setting .fs_images[i] variable of sg_draw_state object, it seems to be impossible to change the output framebuffer, as it is only set when creating sg_pass object, and setting color attachements. As such I don't really see a way to setup buffer ping-ponging in sokol, other than a) injecting openGL or b) creating and destroying pass object on the fly.

My questions are then - have I missed anything and there is in fact a way to set up the ping-ponging framebuffer? Otherwise, given that buffer ping-ponging is relatively common operation, should it be possible to express that operation in sokol without injecting gl code.

Thanks!

Non-interleaved vertex data

Hi,

First of all, thanks so much for the library!
I am currently learning how to use it by writing a simple mesh-viewer application, however I am running into trouble when submitting my vertex data. In my application I am keeping my vertex positions and vertex normals as separate buffers, however all examples showcase interleaved vertex data.

Is there a way to inform sokol about non-interleaved vertex data? (VVVNNN) vs (VNVNVN)

OpenGL pseudocode snipped to show what I am trying to achieve:

    glBufferSubData( GL_ARRAY_BUFFER, 0, mesh->n_vertices*3*sizeof(float), &(mesh->positions[0]) );
    glEnableVertexAttribArray( 0 );
    glVertexAttribPointer( 0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

    glBufferSubData( GL_ARRAY_BUFFER,  3 * sizeof(float) * mesh->n_vertices, 3 * sizeof(float) * mesh->n_vertices, &(mesh->normals[0]) );
    glEnableVertexAttribArray( 1 );
    glVertexAttribPointer( 1, 3, GL_FLOAT, GL_FALSE, 0, (void*) ( 3 * sizeof(float) * mesh->n_vertices) );

sokol_app: Compile error on MSVC + cl

when building with cpp cl compiler, I get error in sokol_app.h, line 2873:

1>error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

here's the place where it happens (function: _sapp_d3d11_destroy_device_and_swapchain):

_SOKOL_PRIVATE _sapp_d3d11_destroy_device_and_swapchain(void) {
    _SAPP_SAFE_RELEASE(IDXGISwapChain, _sapp_dxgi_swap_chain);
    _SAPP_SAFE_RELEASE(ID3D11DeviceContext, _sapp_d3d11_device_context);
    _SAPP_SAFE_RELEASE(ID3D11Device, _sapp_d3d11_device);
}

I guess inserting void on the beginning of the declaration solved the problem

Context-lost-handling for WebGL

This should go into "v1.1".

  • when this happens, put all resources into a new "SG_RESOURCESTATE_LOST"
  • also set a global sokol_gfx 'lost flag'
  • silently skip all rendering operation when in lost state
  • have a D3D9-style function pair to check for lost-flag, and a reset function to notify sokol_gfx that the lost state has been handled
  • app-code needs to call the "sg_init_xxx()" functions to re-initialize resources
  • resource handles and relationships will all be preserved

Make sokol C89 clean

On RaspberryPi with an older GCC (4.9.1) there are a lot of errors because of "for (int...)". If this is the only problem, it's worth it to make sokol_gfx.h C89 clean.

sokol_app: Can't compile with d3d11 + cpp

I'm trying to build sokol_app in a cpp file, but I can't get it done without cl complaining ...

file: d3d11.h , line: 1128

error C2733: 'operator ==': second C linkage of overloaded function not allowed

this is fixed with defining D3D11_NO_HELPERS before including sokol_app.h

But these errors :

error C3861: 'ID3D11DeviceContext_Release': identifier not found
error C3861: 'ID3D11Device_Release': identifier not found
error C3861: 'IDXGISwapChain_GetBuffer': identifier not found
error C3861: 'ID3D11Device_CreateRenderTargetView': identifier not found
error C3861: 'ID3D11Device_CreateTexture2D': identifier not found
error C3861: 'ID3D11Device_CreateDepthStencilView': identifier not found
error C3861: 'ID3D11Texture2D_Release': identifier not found
error C3861: 'ID3D11RenderTargetView_Release': identifier not found
error C3861: 'ID3D11Texture2D_Release': identifier not found
error C3861: 'ID3D11DepthStencilView_Release': identifier not found
error C3861: 'IDXGISwapChain_ResizeBuffers': identifier not found
error C3861: 'IDXGISwapChain_Present': identifier not found

still exist, and I can't get rid of them because the code is disabled in __cplusplus. By inspecting dxgi.h, I defined CINTERFACE and the code is enabled again, but this error shows up:

sokol_app.h(2881): error C2664: 'HRESULT (IDXGISwapChain *,UINT,const IID &,void **)': cannot convert argument 3 from 'const IID *' to 'const IID &'

So I had to add another __cplusplus case for this issue to fix:

#ifdef __cplusplus
    hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt);
#else
    hr = IDXGISwapChain_GetBuffer(_sapp_dxgi_swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp_d3d11_rt);
#endif

PS. there is also a minor performance warning in msvc that is raised and not really important but I though I'd let you know:

warning C4800: 'const char *const ': forcing value to bool 'true' or 'false' (performance warning)

in SOKOL_VALIDATE , sokol_gfx.h (8092)

Running out of resources when creating and destroying buffers with the Metal backend

I'm running into trouble when creating and destroying buffers (or pipelines) every frame with the Metal backend.

Here's a test case made by moving the vertex buffer creation from init to frame in the triangle-metal.c sample and destroying it after using it. After a few seconds it fires an assert:

Assertion failed: (_sg_mtl_free_queue_top > 0), function _sg_mtl_add_resource, file sokol/_sokol_gfx_metal.impl.h, line 379.

One possible error could be with the logic in _sg_mtl_garbage_collect, since it is called after every frame via sg_commit but seems to never process any of the resources in the release queue. Specifically, on line 413 it checks if frame index of the oldest resource is less than the current frame + inflight frames + 1, which should always be true if the frame index only increases and inflight frames is positive.

protection against uninitialized structs

It's easy to forget calling the sg_init_* functions to initialize structs. Solution: add a magic cookie field that's set in the sg_init functions, and check for the correct magic value when the struct is inspected.

Buffer offsets in sg_draw_state?

Add optional buffer offsets to sg_draw_state, this would allow to pack several things into the same buffer.

sg_draw_state ds = {
    .pipeline = pip;
    .vertex_buffers = {
        [0] = vbuf,
        [1] = vbuf,
    },
    .vertex_buffer_offsets = {
        [0] = 128,
        [1] = 2048,
    },
    .index_buffer = ibuf,
    .index_buffer_offset = 256,
    // ...
};

In GL, the offset would be applied during glVertexAttribPointer() calls, in D3D11 in IASetVertexBuffers() and IASetIndexBuffer(), and in Metal in [setVertexBuffer:offset:atIndex] and for indices in [drawIndexedPrimitives:...]

Oh, but it's still illlegal to put vertices and indices into the same buffer (because of WebGL), but this is already checked in _sg_validate_draw_state().

Updating buffers more than once per frame

Hi,

Is there any reason for one update per frame restriction?

I'm implementing a sprite batch using sokol_gfx. A common pattern for this (such as in FNA) is to instantiate a fixed-size array which stores all the vertices and indices of the sprites, and if the user provides more sprites, the sprites are drawn, and the buffers reused for more sprites in the same frame.

Sticking to one buffer update per frame would force me to have a large upper bound on number of sprites which wastes memory, or using heap allocated memory, which is bad for cache.

Removing SOKOL_ASSERT(buf->upd_frame_index != _sg.frame_index); from sg_update_buffer simply works for me. So I'm curious to know what your reasons for this are.

EDIT: This is the code if you're interested in how I'm using sokol_gfx: https://github.com/sherjilozair/sokol2d/blob/master/sokol2d.h#L347

Sokol fails to compile for SOKOL_GLES2

In the function _sg_create_context the line

SOKOL_ASSERT(0 == ctx->vao);

fails to compile when compiling with #define SOKOL_GLES2.

This is not surprising since we have

typedef struct {
    _sg_slot slot;
    #if !defined(SOKOL_GLES2)
    GLuint vao; 
    #endif
    GLuint default_framebuffer;
} _sg_context;

Should be an easy fix. Just a side question; this makes me feel that SOKOL_GLES3 is more tested. Are there maybe more issues related to vao for GLES2? Things seems to work and afaict the sokol-samples are compiled for GLES2...?

Nuklear example

Would be nice to have a nuklear example (instead or over imgui), since nuklear and sokol are C while imgui is C++.

sokol_gfx: Support for multiple contexts/windows

As far as I can see by the implementation, sokol_gfx can not support multiple windows/contexts. Maybe it is just a matter of storing the _sg_backend object and restoring it with a reset_state_cache call between contexts.

[[point_size]] in msl

Not sure if this is really a Sokol problem but referencing
KhronosGroup/SPIRV-Cross#169
it does not seem to be a problem with Oryol?

So the problem is currently, if a msl defines the out struct of the vertex shader with a [point_size]

struct main0_out {
 float gl_PointSize [[point_size]]; // This by default is always in the compiled shader
};

Sokol/Metal complains: Compiler failed to build request
Vertex shader writes point size but inputPrimitiveTopology is MTLPrimitiveTopologyClassTriangle

Could this something specific to the Metal API Validation being enabled?
Currently fixed the problem in the spirv-cross compiler with "opts.point_size_compat = true" but thats not good solution.

Shader reuse over multiple models

Nice API!

I would like to set up soko with one shader setup and render multiple models(verts/index date) with the same format.
I've probably missed something really obvious, but I can't see a way to do this?
I see SG_MAX_SHADERSTAGE_BUFFERS is set to 4 and streaming data via sg_update_buffer is also limited to "... only one update allowed per frame and resource object, and data must be written from the start (but the data size can be smaller than the resource size)."

Any help would be great :)
Cheers
Mike.

Split into .c and .h files

when the .h file is referenced from multiple .c files, compilation performance suffers (on low-end devices: ARM, lowend x86/x64, ...) as well as IDE performance. Splitting it to .c and .h files would reduce that enormously.

PROS:

  • Compilation Time
  • Linting/intellisense performance
  • Ability to package sokol into a shared library
  • Reduce readability overhead
  • Having a shared library, makes it easy to make wrappers for other languages

If you still have to do that, you can include one intermediate header file that include both .c and .h files

Note: Nuklear started by having a header only file, then ended up doing the split

Visualize depth buffer

Hi, thanks for creating sokol, I find very refreshing working with it!
Question:
do you have somewhere a snippet of code or an example on how I can access/visualize the depth buffer?
I naively tried to attach the depth_image to a color attachment but it doesn't seem to work for me.

For example, in the offscreen-glow.c sample, if I modify the default draw state like this (around line 242):

    sg_draw_state default_ds = {
        .pipeline = default_pip,
        .vertex_buffers[0] = vbuf,
        .index_buffer = ibuf,
        .fs_images[0] = depth_img // was: "color_img"
    };

I see no difference in the generated output image.

Similarly, using the mrt-glfw.c sample, if I change the debug view in the main loop to also draw the depth, like this (around line345):

        sg_draw(0, 4, 1);
        for (int i = 0; i < 3; i++) {
            sg_apply_viewport(i*100, 0, 100, 100, false);
            dbg_ds.fs_images[0] = offscreen_pass_desc.color_attachments[i].image;
            sg_apply_draw_state(&dbg_ds);
            sg_draw(0, 4, 1);
        }
        for (int i = 3; i < 4; i++) {
            sg_apply_viewport(i*100, 0, 100, 100, false);
            dbg_ds.fs_images[0] = offscreen_pass_desc.depth_stencil_attachment.image;
            sg_apply_draw_state(&dbg_ds);
            sg_draw(0, 4, 1);
        }
        sg_end_pass();

what I get is a duplication of the last color attachment that has been bound (see the following screenshot).
screen shot 2018-03-26 at 7 00 37 am

Probably I'm trying to "map" my OpenGL mindset to Sokol, and doing something wrong.
Any suggestion on the right way to use the depth buffer in a shader?

thanks!

pass vs pipeline validation

sg_apply_draw_state() must check:

  • if pipeline mrt_count matches pass number of color attachments
  • if pipeline color_format matches pass color attachments pixel format
  • if pipeline depth_format matches pass depth format
  • if pipeline sample count matches pass attachment sample count

Cleanly separate the GL3.3, GLES2 and GLES3 backends variations.

The GL backend variations are currently "leaking" into each other, e.g. the GLES2 backend is using functionality that isn't actually available in GLES2.

  • if SOKOL_GLES2 is set, only use GLES2 functionality
  • same for GLES3
  • provide the ability to fallback to GLES2 if GLES3 could not be initialized (for WebGL2 vs WebGL)
  • ...on emscripten with WebGL1, use the proper GLES2 emscripten headers

content initialization/update for immutable buffers and images

  • rename 'data_ptr' to 'content' everywhere
  • in sg_image_desc, replace the data pointer-pointers with a simple 2D array (faces and mipmaps), this is ok since 2D-array-slices and 3D-texture-slices are provided 'en-bloc', and not through unique pointers
  • define a new sg_image_content struct for sg_update_image() which holds the subimage pointers and sizes (would make sense to use this also embedded in the sg_image_desc struct)

resource pools todo

Cleanup the 'item id 0 is reserved' stuff:

  • must allocate one more item then requested
  • size of the free-index queue is requested size, and doesn't have index 0 in it

Assert break in sokol_gfx init: Linux + glew 1.13

Hi
I'm on ubuntu with installed package glew 1.13.
I got assertion on the first _SG_GL_CHECK_ERROR(); in _sg_create_context function. because there was a pending GL error (GL_INVALID_ENUM)
It seems that the problem is with version 1.13 of glew documented here

So I had to reset the errors after glewInit. Is it ok if sokol_gfx clears errors on setup and throw a warning or something, in case if previous errors are raised by glew or similiar lib ?

Failed assertion with texture samples in the metal backend

When running the sapp dear imgui sample with xcode/metal the app crashes. The strange thing is that it worked a day ago with no problem...

2018-05-30 15:32:49.223915+0200 imgui-sapp[1812:27662] [DYMTLInitPlatform] platform initialization successful
2018-05-30 15:32:49.461297+0200 imgui-sapp[1812:27593] Metal GPU Frame Capture Enabled
2018-05-30 15:32:49.461684+0200 imgui-sapp[1812:27593] Metal API Validation Enabled
2018-05-30 15:32:49.739457+0200 imgui-sapp[1812:27684] MessageTracer: Falling back to default whitelist
2018-05-30 15:32:50.341674+0200 imgui-sapp[1812:27593] -[MTLTextureDescriptorInternal validateWithDevice:], line 832: error 'MTLTextureDescriptor has sampleCount set but is using a type that does not allow sampleCount.'
-[MTLTextureDescriptorInternal validateWithDevice:]:832: failed assertion `MTLTextureDescriptor has sampleCount set but is using a type that does not allow sampleCount.'

sokol_app.h: text input handling on mobile HTML5

Problems in the current implementation:

  • on iOS11 (Safari) and Android (Chrome) mobile phones, the canvas is zoomed when the onscreen keyboard is opened (and not restored to the original size when the keyboard closes)
  • on Android, no character input is detected from the keyboard, Backspace seems to work
  • all sorts of breakage on iOS12 Beta 3 (after opening the keyboard once):
    • changing between portrait and landscape doesn't seem to change the horizontal pixel resolution, only the vertical
    • changing between portrait and landscape doesn't get reported to emscripten
    • when opening the keyboard, the canvas is squished vertically

missing validations

  • sg_apply_draw_state: must check whether sample_count in pipeline matches current render pass sample count

GL backend todo

  • apply-draw-state: state caching for vertex attribute definition
  • apply-draw-state: bind textures
  • extension check, sg_query_feature(), instancing functions
  • error codes and messages
  • _sg_create_shader: resolve uniform and texture locations (named and 'opaque')
  • create image
  • create pass
  • query default frame buffer at start
  • MRT passes
  • draw-elements-instanced
  • cleanup resources on shutdown
  • viewport and scissor rect functions are totally missing

Test, please ignore

sokol/sokol_gfx.h

Lines 375 to 392 in 2070f2b

typedef enum {
SG_FEATURE_INSTANCING,
SG_FEATURE_TEXTURE_COMPRESSION_DXT,
SG_FEATURE_TEXTURE_COMPRESSION_PVRTC,
SG_FEATURE_TEXTURE_COMPRESSION_ATC,
SG_FEATURE_TEXTURE_COMPRESSION_ETC2,
SG_FEATURE_TEXTURE_FLOAT,
SG_FEATURE_TEXTURE_HALF_FLOAT,
SG_FEATURE_ORIGIN_BOTTOM_LEFT,
SG_FEATURE_ORIGIN_TOP_LEFT,
SG_FEATURE_MSAA_RENDER_TARGETS,
SG_FEATURE_PACKED_VERTEX_FORMAT_10_2,
SG_FEATURE_MULTIPLE_RENDER_TARGET,
SG_FEATURE_IMAGETYPE_3D,
SG_FEATURE_IMAGETYPE_ARRAY,
SG_NUM_FEATURES
} sg_feature;

Buffer Offsets TODO

  • D3D11 sample
  • Metal sample
  • emscripten sample
  • update Nim bindings
  • update samples webpage

Image Export

Hi Andre,
I use sokol since few weeks and (for now) I think the only missing feature is the export of image to CPU side.
I don't known if this request is compatible with Metal and GLES API but I think it's Ok for DX11 and OpenGL.
And maybe you don't want to add new feature to your lib?
Anyway, thanks for this awesome little and simple lib.

Backend state inconsistencies and missing states

  • Metal doesn't seem to have a scissor-test-enabled state, consider removing this from the sokol API (scissor test would always be enabled, and the scissor rect would be reset in sg_begin_pass)
  • texture anisotropy
  • depth bias / polygon offset
  • D3D11 doesn't have signed 10-10-10-2 vertex format, but unsigned version

calling sg_reset_state_cache produces opengl error 0x502

Every frame I'm rendering I mix sokol with other opengl related libraries, not a good Idea I know, but sokol is supposed to handle that with sg_reset_state_cache, doesn't it?

the order is:

  1. render with sokol
  2. render with another_lib (nanovg)
  3. frame
  4. call sg_reset_state_cache
    --> something inside sg_reset_state_cache produces OpenGL error 0x502
  5. sokol refuses to render due to openGL error

If I clear openglErrors between 4 and 5, it works... but something is not right.

(Thanks for an awesome library, btw ๐Ÿ‘ )

PD: The proper way is to implement a nanovg backend on top of soko_gfx, I know! Just need time.

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.