Code Monkey home page Code Monkey logo

pillarbox-apple's Introduction

Pillarbox logo

Overview

GitHub releases platform SPM compatible GitHub license

Pillarbox is the iOS and tvOS modern reactive SRG SSR player ecosystem implemented on top of AVFoundation and AVKit. Pillarbox has been designed with robustness, efficiency and flexibilty in mind, with full customization of:

  • Metadata and asset URL retrieval.
  • Asset resource loading, including support for FairPlay.
  • Analytics and QoS integration.
  • User interface layout in SwiftUI.

Its robust player provides all essential playback features you might expect:

  • Video and audio playback.
  • Support for on-demand and live streams (with or without DVR).
  • First-class integration with SwiftUI to create the stunning playback user experience that your application deserves.
  • Integration with the standard system playback user experience, both on iOS and tvOS.
  • Playlist management including bidirectional navigation.
  • Support for alternative audio tracks, Audio Description, subtitles, CC and SDH, all tightly integrated with standard system accessibility features.
  • AirPlay compatibility.
  • Control center integration.
  • Multiple instance support.
  • Best-in-class Picture in Picture support.
  • The smoothest possible seek experience on Apple devices, with blazing-fast content navigation in streams enabled for trick play.
  • Playback speed controls.

In addition Pillarbox provides the ability to play all SRG SSR content through a dedicated package.

Showcase

Here are a few examples of layouts which can be achieved using Pillarbox and SwiftUI, directly borrowed from our demo project:

Showcase

From left to right:

Code example

With Pillarbox creating a custom video player user interface has never been easier. Simply instantiate a Player and start building your user interface in SwiftUI right away:

import Player
import SwiftUI

struct PlayerView: View {
    @StateObject private var player = Player(
        item: .simple(url: URL(string: "https://devstreaming-cdn.apple.com/videos/streaming/examples/img_bipbop_adv_example_ts/master.m3u8")!)
    )

    private var buttonImage: String {
        switch player.playbackState {
        case .playing:
            return "pause.circle.fill"
        default:
            return "play.circle.fill"
        }
    }

    var body: some View {
        ZStack {
            VideoView(player: player)
            Button(action: player.togglePlayPause) {
                Image(systemName: buttonImage)
                    .resizable()
                    .frame(width: 80, height: 80)
            }
        }
        .onAppear(perform: player.play)
    }
}

With the expressiveness of SwiftUI, our rich playback API and the set of components at your disposal you will have a full-fledged player user interface in no time.

Compatibility

The library is suitable for applications running on iOS 16, tvOS 16 and above. The project is meant to be compiled with the latest Xcode version.

Contributing

If you want to contribute to the project have a look at our contributing guide.

Integration

The library can be integrated using Swift Package Manager directly within Xcode. You can also declare the library as a dependency of another one directly in the associated Package.swift manifest.

A few remarks:

  • When building a project integrating Pillarbox for the first time, Xcode might ask you to trust our plugins. You should accept.
  • If you want your application to run on Silicon Macs as an iPad application you must add -weak_framework MediaPlayer to your target Other Linker Flags setting.

Getting started

To learn more how integration of Pillarbox into your project please have a look at our getting started guide.

Documentation

Follow the links below for further documentation:

License

See the LICENSE file for more information.

pillarbox-apple's People

Contributors

defagos avatar mbruegmann avatar waliid avatar

Stargazers

 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

pillarbox-apple's Issues

Add stale / inactivity triage for issues

  • Add issue label for stale status.
  • Set as stale via bot after some inactivity period (with warning), then close as won't fix if ignored.
  • Maybe set exceptions for some labels (see stale documentation e.g.).

Ensure reliable DVR detection

As a user I never want to be able to seek when playing a livestream.

Context

AVPlayer drops the time corresponding to 3 segments when calculating the available time range. For livestreams this should in general lead to a zero window, but if non-rounded segment durations are allowed (which is the default with playlist V3 format) you can have live playlists that look as follows:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:2
#EXT-X-MEDIA-SEQUENCE:3
#EXTINF:2.002000,
chunk3.ts
#EXTINF:2.002000,
chunk4.ts
#EXTINF:2.002000,
chunk5.ts

In this case the calculated duration is 3 * 2.002 - 3 * EXT-X-TARGETDURATION = 0.006. If we check for an empty range this stream is seen as supporting a DVR, which is obviously not the desired experience.

For this reason test streams are currently with integer segment durations (ffmpeg round_durations flag). This should obviously not be needed.

Note that the RFC might provide useful hints about such rounding issues.

Acceptance criteria

  • Livestreams are reported as livestreams, even when non-integer segment durations are allowed.

Tasks

  • Remove round_durations from the stream generation script and check that playlists with small deviations from integer values are produced.
  • Find a good strategy to avoid rounding issues to make all UTs pass.

Set minimum deployment target to iOS / tvOS 16

As an integrator I want to be able to integrate Pillarbox in projects running iOS 16, tvOS 16 and above.

Acceptance criteria

  • The project can be integrated in projects running iOS 16 and tvOS 16 only.
  • The demo runs on iOS 16 and tvOS 16.

Tasks

  • Update package format to 5.7.
  • Update package file to iOS 16 and tvOS 16.
  • Update demo to iOS and tvOS 16.
  • Update readme.
  • Use modern Xcode 14 project format.
  • Move to NavigationStack.

Generate Core documentation

As an integrator I want documentation for all packages to be available.

Acceptance criteria

  • The Core documentation can be generated.

Tasks

  • Add Core documentation generation to the script.
  • Check the documentation appears when generated from Xcode.

Fix nightly submission

As a developer I want to be able to automatically nightlies to test PRs and validate status checks.

Acceptance criteria

  • Nightlies are delivered without any issues.

Tasks

  • fastlane is updated to solve the App Store Connect issue which appeared after iOS 16 has been released.
  • GitHub maximum branch protection is restored (mandatory nightly builds).

Make tests run faster on the CI

As a developer I would like to have CI check output faster if possible.

Acceptance criteria

  • UTs run faster than currently (~5 min).

Tasks

  • Update fastlane.
  • Run Pillarbox-Package scheme for UTs.
  • Verify JUnit output.

Setup Xcode project

Create a new project for Pillarbox:

  • Swift package with expected 3rd party dependencies
  • Demo target
  • Test target
  • Basic readme
  • Basic documentation bundles
  • Basic contribution guides
  • Issue / PR templates
  • Makefiles
  • Linter
  • Setup repository branch protection policies
  • Setup code signing

Check:

  • All targets run on iOS and tvOS devices and simulators
  • Package can be integrated in a new project from the GitHub repository
  • Documentation can be generated and browsed

Improve UT reliability

As a developer I want unit tests to be as reliable as possible. Multiple seek tests in particular are not that reliable:

DequeuePlayerNotificationTests.testMultipleSeeksWithCompletionHandlerDuringPlayback
DequeuePlayerNotificationTests.testMultipleSeeksAsyncDuringPlayback

Acceptance criteria

  • Unit test suites run reliably for iOS and tvOS.

Tasks

  • Locally run the tests repeatedly with Xcode repeat feature.
  • Check CI Xcode version used. Upgrade if not the latest Xcode 14 beta.
  • Attempt to improve the UT implementation.
  • Otherwise increase the number of retries with a test plan.

Remove sound from test streams

As a developer who loves NyanCat, hearing it again and again when running tests is becoming tiresome.

Acceptance criteria

  • Mute the sound of test videos.

Tasks

  • Strip the audio out with ffmpeg.
  • Check that UTs pass.

Play unprotected SRG SSR contents from URNs

As an integrator I want to be able to play any unprotected Play SRG content via its URN.

Acceptance criteria

  • The API allows content to be played from a URN.
  • Production, stage and test environments are supported.
  • The first available resource is played (no complicated resolution logic as currently in Letterbox).
  • URN-based content is playable from the demo (keep URL-based examples as well).

Tasks

  • Parse media composition (basic).
  • Implement resource loader.
  • Provide AVPlayerItem constructor based on URN.
  • Add support for IL environments.
  • Keep only supported resources (HLS, MP3) based on the streaming resource property.
  • Add URN examples in the demo (urn:rts:video:6820736, urn:rts:video:8393241, urn:rts:video:8412286, urn:rsi:audio:8833144) at the end of the list.

Handle buffering and seeking states

As a user I want the player to see when the player is seeking or buffering.

Acceptance criteria

  • Seeking and buffering states are supported.
  • The player displays a basic spinning wheel when in these states.

Tasks

  • Carefully study buffering-related APIs.
  • Add seeking and buffering states (might be the same).
  • Check behavior after network loss or when returning from background.
  • Add basic UI in demo.
  • Add empty player demo.
  • Add failure demo.
  • Ensure correct behavior for the empty and failure cases (write tests first).
  • Implement basic HTTP error management (must avoid exceptions thrown in this case when getting the media composition).
  • Write unit tests for seek behaviors at the player state level (omitted when implementing these behaviors in the first place).

Add basic demo for stories and multiplayer

As a developer I need to be able to identify pain points early, especially regarding performance. Having demos for a story-like integration and for simultaneous playback would be valuable.

Acceptance criteria

  • A basic demo for stories is available.
  • A basic multiplayer demo is available.
  • Demos to connect a player to a view are available.

Tasks

  • Add story demo.
  • Add demo in which a player can be attached or detached from a view.
  • Add demo in which a player can be instantiated and attached to a view, or nilled.
  • Add multiplayer demo (different contents).
  • Add multiplayer demo (same content but displayed in view A, B or A & B).

Update Fastlane to remove Xcode 13 need

As a developer I don't want to use several versions of Xcode to build the project on the CI server.

Acceptance criteria

  • Only Xcode 14 is used to build the project on the CI.

Tasks

  • Update Fastlane.
  • Update Fastfile to remove Xcode 13 references.

Investigate leak with MP4

In the simulator the player leaks when seeking a bit randomly in the current MP4 example until the image freezes. Once the image is frozen dismissing the player does not release resources and playback continues in the background.

Basic playback of a Swissinfo content in the demo app

As a user I can play a Swissinfo content in the demo application with a minimalist user interface.

Acceptance criteria

  • The player plays the following content.
  • The demo application displays the video to the user.
  • Playback starts without user intervention.
  • The user can pause / resume playback with a button.
  • The user can seek anywhere in the video with a slider.

Tasks

  • Add basic queue player with basic playback API.
  • Add raw video layer view.
  • Add demo entry to play the SWI content.
  • Implement player states (to be determined).
  • Implement playback time observers.
  • Introduce test streams and document setup.

iOS only (tvOS is unspecified as we will use the native player UI anyway):

  • Display play / pause button in basic player layout.
  • Display slider.

Align demo presentation

As an integrator I would like to have consistent demos between Android and iOS where applicable.

Acceptance criteria

  • The iOS and Android demos have been aligned where applicable.

Tasks

  • Discuss demo structure and content with the Android team.
  • Update the existing demo on iOS and tvOS.

Make repository public

  • Update CI documentation (tokens with no private access to repositories).
  • Remove repo rights for GitHub access tokens currently used (RTS devops) or duplicate token (best option required).
  • Toggle repository visibility to public.
  • Toggle Project board visibility to public.
  • Replace existing repository template with latest version.

Setup CI

  • Setup private configuration repository
  • Setup project version and local xcconfig files
  • Ensure bundle id / name reflect nightly builds (name, emoji)
  • Add Fastlane support
  • Create nightly and beta app on TestFlight
  • Create automatic release notes
  • Setup project on TeamCity
  • Ensure test reports can be properly browsed on TeamCity
  • Ensure test reports are properly cleaned at start so that each run delivers its associated results only
  • Add badges to icons with fastlane
  • See if integrating Danger is valuable (e.g. spellchecking, UT result, check for print statements, etc.)
  • Post UT status to PR.
  • Integrate Jazzy
  • Implement command to add testers from fastlane
  • Document Makefile local use and CI integration
  • Ensure the project works without private configuration

Fix seek bar behavior with MP4 stream

As a user I want proper seek behavior with MP4 streams (no hiccups). A regression was introduced with #42.

Acceptance criteria

  • Open the MP4 video example, grab the slider and throw it left and right. The slider must not jump erratically.

Tasks

  • Write UT with MP4 example reproducing the issue.
  • Fix the issue.

Replace Tag Commander SDK with V5

As a developer I need to check that the Tag Commander V5 SDK can be integrated with our player ecosystems. Integration in Pillarbox is enough to give an answer.

Acceptance criteria

  • The Tag Commander V5 SDK is integrated in Pillarbox (Core 5.1.1, ServerSide 5.1.2). This SDK includes support for tvOS.
  • iOS and tvOS demos build and run successfully.
  • iOS and tvOS demos can be distributed via TestFlight.

Tasks

  • Package the new SDKs with SPM (new repositories probably). Package both SDKs with a single manifest and repository if possible.
  • Integrate in package manifest.
  • Briefly test calling the API from the code (no commit).
  • Report to Tag Commander team.
  • Demos can be successfully built and distributed via TestFlight.

Stricter linting rules

Now that we have some linting in place we can put strict linting rules in place. Play SRG codebase is the perfect place to fine-tune these rules (and offer improvements to Play SRG at the same time):

  • Update rules based on Play.
  • Transpose rules to Pillarbox (and make them even stricter if possible).
  • Fix code bases with automatic / manual linting.

Backward playlist navigation

As a user I want to be able to move to the next item in a playlist, but also to move back to previous items.

Acceptance criteria

  • The API has been updated to support navigation to a previous item in a playlist.
  • The demo provides a basic playlist example with forward / backward navigation.

Tasks

  • Check behavior when playback of the last item ends normally. Is the current item turned to nil? Can we prevent it? (see issue below)
  • Add canAdvanceToNextItem and canReturnToPreviousItem APIs? Possible? (depends on answer above).
  • Implement better thread-safety (@MainActor only checks overridden or subclass methods). Avoid subclass and use composition for the deque. Expose the raw player since we don't want to forward every method and we still need to do KVO on the player. Alternative: Have Player play this role and rename DequePlayer as RawPlayer, which will only deal with seeking (still required for consistent behavior since seeking can be triggered by the system). Think whether @MainActor should be replaced with a manually managed serial queue instead for the reasons mentioned above.
  • Update main player API accordingly (expose playlist management methods).
  • Update demo.
  • Reset started playlist items at the beginning when navigating away? (opt-out?) See comments below
  • Create player item constructor for dummy items by name, replacing e.g. AVPlayerItem(url: URL(string: "https://www.server.com/item1.m3u8")!) with AVPlayerItem(name: "item1").
  • Have inserts and moves return a bool.
  • Check documentation generation.
  • Make slider not draggable (i.e. progress not updatable) when currentItem is nil.
  • Add API for AVPlayerActionAtItemEnd. Premature, see comments below
  • Check backwards navigation works in background (probably required: Insert previous item after current one and advance).
  • Fix demo issue with URN resource loading which does not work correctly, leading to an incorrect behavior (demo works fine with URLs, though).
  • Fix race conditions with propertyPublisher reported after the playlist API has been moved to Player. See comments below
  • When a resource loader is used for items in a playlist, all items apparently have to be retrieved before playback of the first item can start. This must be mitigated if possible (maybe a shared resource loader is the key, since we currently have one per item). Opened as #81 since probably a combination of item storage for playlist backward navigation and incorrect resource loadind.
  • Check memory profile and network activity when navigating in the playlist.

Set minimum version to 16.1

Don't do if #81 solves issues on iOS 16.0, as this would not be required.

Also see #76.

Tasks

  • Update in package manifest.
  • Update Fastfile to use Xcode 14.1.
  • Update in demo.
  • Update documentation.

Improve notification center publishers

For more consistency we should make the filter object of weak notification publishers nullable:

  • Make object parameter nullable.
  • Write unit tests to validate memory deallocation.

Organize code in packages

As an API user I want to find APIs in the proper package for ease of use.

Acceptance criteria

  • UI types are moved to the UserInterface package.
  • A new package (name to be determined) is introduced for convenience tools (publishers, bindings, etc.)

Tasks

  • Move UI classes to UserInterface.
  • Decide package name
  • Create new package and configure project.
  • Move sources and tests to the new package.

Improve type equality

As a developer I want to be able to compare types in the most reliable way possible.

Acceptance criteria

  • Update Equatable types equality implementation to take into account associated values.

Tasks

  • Update ItemState.
  • Update PlaybackState

Hint

Cast Swift errors to NSErrors for comparison.

Add seek modes

As an integrator I want to be able to implement an audio player which either seeks continuously during playback or when the user releases their finger.

See SRGSSR/pillarbox-documentation#11.

Acceptance criteria

  • The player provides a way to choose between both modes (default: continuous, the current standard).

Tasks

  • Define an enum for the two modes.
  • Implement.

Provide demo test and sample streams with constants

As a developer I want to have consistent demo and test streams which I can easily update in a single spot if needed.

Acceptance criteria

  • Demo and test streams are available through constants.

Tasks

  • Introduce constants for demo and test streams.
  • Update demo and test to use these streams.
  • Maybe introduce constants for the duration of streams (e.g. 120 seconds) which can be consistently updated as well.

Remove Appearance package

As an SDK user I want a clean separation of packages, as documented, without unnecessary redundancies.

Acceptance criteria

  • The Appearance package is removed (likely part of UserInterface).

Tasks

  • Remove Appearance from products.
  • Remove UTs.
  • Remove in Fastfile and scripts.

Build demos as part of status checks

We should check that the demos can be built and archived as part of basic status checks (no need to deliver to TestFlight):

  • Update Fastfile and Makefile to add archive support (both demos).
  • Add dedicated configurations on TeamCity.
  • Update the CI documentation.

Improved seek user experience

As a user I want to have a good seeking user experience. As an integrator I want to have a simple way to connect a player to a standard system slider.

Acceptance criteria

  • Seek works fine without hiccups or issues.
  • Integration in an app is simple.

Tasks

  • Improve the progress reporting API.
  • Avoid seek bar hiccups while grabbing the slider and moving slowly.
  • Ensure correct behavior when loading with slow connection.
  • Ensure the slider is not seekable if there is no time range (and located at 1).
  • Update the demo.

Lint scripts and markdown files

We should lint Ruby (rubocop), Shellscripts (shellcheck) and Markdown (TBD):

  • Add local shell scripts to call linters, failing with a message if commands are missing:
    • Ruby
    • Shell
    • Markdown
  • Add manifests (if any is required for rule consistency) and edit rules in a strict way:
    • Ruby
    • Shell
    • Markdown
  • Add local make targets to run each linter separately.
  • Let make / fastlane commands be run without Configuration folder where possible.
  • Add .editorconfig.
  • Install tools on CI servers (one build step per command).
  • Add TeamCity output processors if available.
  • Remove Danger (finally not used that much):
    • Remove lanes.
    • Remove private configuration.
    • Revoke GitHub token.
    • Remove environment variables on TeamCity.
  • Update CI configuration documentation.
  • Document local setup (tools to install, integration with VSCode, etc.).

Boundary time and asset property state publishers

As a developer I need a complete toolbox to implement player internals in a reactive manner. As an integrator I need to have standard player APIs for accessing internals in a safe way.

Acceptance criteria

  • Boundary time observation is provided.
  • Safe reactive access to asset properties is provided.

Tasks

  • Investigate if signposts can be implemented to visualize player updates in Instruments (maybe a simple handleEvents suffices).
  • Cleanup playback property publisher when seeking (write tests first).
  • Add boundary time publisher (similar to the existing periodic time publisher).
  • Implement asset property publisher with the modern iOS 15 async API.
  • Provide generic asset property publisher variants matching the iOS 15 async API ones.

Unexpected background streaming activity

As an application user I don't want applications playing audio or video content to load data unnecessarily in the background.

Acceptance criteria

  • The following issue with the Stories demo has been fixed:
    • Open a proxy tool to monitor the traffic.
    • Swipe between stories fast. At most 3 streams should be active but the proxy shows all of them are until they are completely loaded.
  • When disconnecting a player from its view subtitles (e.g. forced subtitles in Apple streams) might remain. This must not be the case and might either be related to the same issue or an iOS issue to report.

Hints

  • Same result on device and in the simulator.
  • The issue is likely related to @ObservedObjects and their lifecycle. If we namely store the AVQueuePlayer in the VideoView implementation the issue disappears (but remains visible if we drop a slider with a Player parameter onto the view, e.g.).
  • No memory leak occurs, only unnecessary remaining activity which shouldn't. Likely not a release issue, rather we should find how the lifecycle of unused players can be reduced in the context of SwiftUI.

Tasks

  • Fix streaming activity issue.
  • Check whether the subtitle issue is then fixed or report to Apple.

Optimize event delivery

As a user I want as few hiccups as possible when playing content.

Acceptance criteria

  • Existing event sources have been investigated and work has been moved to background threads where still possible.

Tasks

  • Investigate each data pipeline.
  • Move to a background thread where appropriate.

Basic livestream playback

As a user I want to play all type of unprotected audio & video livestreams provided by SRG SSR.

Acceptance criteria

  • Demo available to play audio and video livestreams.
  • Display Play / Pause buttons.
  • Protected content (DRM / Token) not included.
  • No timestamp management.

Tasks

  • Find if local delivery with Python is possible for UTs.
  • Update documentation (in particular the need to start the server manually for local development).
  • Update 404 stream (point at missing local stream).
  • Improve test server management (command to start / stop the server and the streams; start before UTs and stop after).
  • Generate files in /tmp directory to avoid Xcode entering a package update loop.
  • Correctly identify livestreams (short enough time range).
  • Update demo to add livestream examples (video / audio).
  • Hide progress bar.
  • Fix crash when seeking right after on-demand playback start (likely unknown time range).

Add basic demo and playground

As an library user I want examples showing the simplest integration possible.

Acceptance criteria

  • A basic example is added to the demo.
  • A playground with a minimal example is provided.

Tasks

  • Add basic demo.
  • Add basic playground.

Basic DVR livestream playback

As a user I want to play all type of unprotected audio & video DVR livestreams provided by SRG SSR.

Acceptance criteria

  • Demo available to play audio and video DVR livestreams.
  • Display Play / Pause buttons.
  • Protected content (DRM / Token) not included.
  • No timestamp management.

Tasks

  • Find if local delivery with Python is possible for UTs.
  • Correctly identify livestreams (large enough time range, moving).
  • Create basic player configuration (defaut configuration with builder to tweak settings like DVR window e.g.).
  • Update demo to add livestream examples (video / audio).
  • Bind progress value to progress bar.
  • Have correct progress information reported.

Fix resource loading conflicts with playlist management

As a user I want a good user experience with any kind of content.

Acceptance criteria

  • Playback starts fast.
  • Playback never unexpectedly continues in background after closing the player.

Hints

A custom resource loader was implemented in #60 to load content from URNs and deque playlist support was added with #76. When combined these two features show several issues:

  • On iOS 16.0 navigating back does not work (navigation is made forward). This works on iOS 16.1 but maybe this is not an actual iOS issue but rather an artefact of our implementation, leading to unreliable behavior.
  • When adding some delay to the media composition pipeline (e.g. 4 seconds) playback only starts when resource loading requests for all items in the playlist have been made. Loading the item from an asset with automaticallyLoadedAssetKeys set to ["playable"] makes the player start early but, if the player is closed while some items are still not loaded, the player continues to play in the background until all pending resource loader requests have been processed.

Remarks:

  • We could share resource loaders among items (lazy instantiation and reuse, per environment).
  • Resource loading cancellation is not called often it seems.
  • The issue seems similar to those we sometimes experience with Letterbox after closing the player, still hearing the sound for a while. Letterbox also stores the item internally and has a resource loader working in a similar way so we might find a way to improve Letterbox at the same time.

Tasks

Probably better investigated in a project implemented from scratch. An idea:

  • Mitigate reported resource loader issues.
  • Check memory and network profile.
  • Make PR for Letterbox if an improvement is found.
  • Check UT resilience if automaticallyLoadedAssetKeys is empty.
  • Check if AVAssetResourceLoadingContentInformationRequest can be meaningfully filled.

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.