Code Monkey home page Code Monkey logo

wimboot's Introduction

wimboot: Windows Imaging Format bootloader

Build Coverity Tests Release

wimboot is a boot loader for Windows Imaging Format .wim files. It enables you to boot into a Windows PE (WinPE) deployment or recovery environment.

You can use wimboot with iPXE to boot Windows PE via HTTP. With a Gigabit Ethernet network, a typical WinPE image should download in just a few seconds.

Demo

This animation shows an HTTP network boot into the Windows 10 installer. The total elapsed time from power on to reaching the Windows installer screen is 17 seconds.

Demo animation

Advantages

Speed

wimboot can download images at the full speed supported by your network, since it can use HTTP rather than TFTP.

Ease of use

wimboot works directly with .wim image files; there is no need to wrap your .wim into an ISO or FAT filesystem image.

BIOS/UEFI compatibility

wimboot allows you to use a single configuration and set of files to boot under both BIOS and UEFI environments.

License

wimboot is free software licensed under the GNU General Public License.

Quickstart

See https://ipxe.org/wimboot for instructions.

wimboot's People

Contributors

a1ive avatar alive4ever avatar asheplyakov avatar mcb30 avatar mexit avatar rgl avatar sigeryang 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

wimboot's Issues

UEFI Grub2 how to use wimboot

Does UEFI Grub2 not use wimboot? Legacy mode is OK. It seems that UEFI mode must use ipxe, but ipxe is not friendly to local disk boot

Filename requirements should be documented

When I first started setting up wimboot 2.6, I noticed and documented some quirks, mainly about using custom scripts in consistent way for a dual Legacy BIOS and UEFI usage. I don't think any of these are serious bugs, but it is stuff that I had to discover with trial and error because it was undocumented or unclear in the documentation. Sorry if any of these are actually documented, I may have just rewritten a few things to be clear reminders in my face in the future if editing my wimboot config.

Since it looks like wimboot is now back in a development phase, I thought I would share these notes in case any of them seem worth addressing, or at least officially documenting as standard behavior.

I am not sure if any of these behaviors have already changed after version 2.6 since my current config works on 2.7.1 and I have not retested all these things to try to my break wimboot or my loading of custom scripts.

Notes about adding custom script files and naming:

  • CUSTOM SCRIPTS can be dynamically added into WinPE and MUST be loaded BEFORE the .wim is loaded.
  • CUSTOM SCRIPTS can ONLY be added into \Windows\System32, any custom path specified after a URL as described in https://ipxe.org/cmd/imgfetch is used as a filename instead of a path in Legacy BIOS mode and is ignored in UEFI mode.
  • In Legacy BIOS mode, for wimboot to load properly, each script MUST have a name specified after the URL (which will also set the filename in the filesystem) even if the file doesn't need to be renamed.
  • Also, in Legacy BIOS mode, because of needing to supply a filename as a argument, it appears that there is no way to use a filename that has spaces. So if a filename has spaces it MUST be renamed to not have any spaces.
  • In UEFI mode, the names after the URLs are not required and are ignored (they do not rename the file). If a file needs to be renamed, initrd's "-n" arg can be used.
  • In Legacy BIOS mode, the specified name after the URL is used even if a different name is set with "-n".
  • In UEFI mode, the original filename will be used or a custom filename if one is set with initrd's "-n" arg.
  • Since different args modify filenames differently for Legacy BIOS and UEFI mode it's very important to make sure filenames will be set the same in both environments.

This one is probably the most like an actual bug and was discovered when adding an empty flag file to put my own install script into a "testing mode" with extra debug logging:

  • For some reason if this is an empty 0 byte file it will make wimboot in Legacy BIOS hang forever (so just put any text in the file).

Notes about .wim, BCD, and boot.sdi files:

Please let me know if you need any clarification or examples about any of these notes.

Failure to start with error 0xc000000d (Win8/Win10 32-bit, >4GB RAM)

Raised from #13

The following .yml file defines a test case that can reproduce the problem:

name: Windows 10 (High RAM, 32-bit)
version: win10
arch: x86
memory: 5120
logcheck:
  - 'Found initrd at \[0x[0-9a-f]{8},0x[8-9a-f][0-9a-f]{7}\)'
  - 'Placing initrd at \[0x[0-9a-f]{8},0x[0-7][0-9a-f]{7}\)'
  - 'Placing initrd at physical \[0x[1-9a-f][0-9a-f]{8,},'

This test fails for Windows 10 (or Windows 8.1) with a 0xc000000d error:
c000000d

The test passes if using any 64-bit version of Windows, or if using 32-bit Windows 7.

ARM Windows boot

Description

As a user, I would like to be able to boot ARM versions of Windows via wimboot, so that I can take advantage of HTTP boot.

Acceptance criteria

  • Ability to boot into some version of Windows on 32-bit ARM via qemu+OVMF+iPXE+wimboot
  • Ability to boot into some version of Windows on 64-bit ARM via qemu+OVMF+iPXE+wimboot

Dependencies

Command-line debug shows only first command-line argument

Commit 9c576c7 deferred the printing of the command line until after parsing the quiet command-line argument, in order to allow the printing of the command line to itself be inhibited.

The act of parsing the command line will replace the argument separator characters with NULs, with the result that only the first command-line argument will now be printed.

efireloc update to match iPXE elf2efi

Description

As a developer, I would like wimboot and iPXE to use identical tools for creating PE executables, so that I have only a single tool to maintain.

Acceptance criteria

  • wimboot's efireloc.c and iPXE's elf2efi.c are unified into a single tool
  • wimboot continues to produce a hybrid 32-bit BIOS and 64-bit UEFI binary

Failing to Extract BCD & boot.sdi

Awesome to see new releases of wimboot, thanks so much! I'm glad to have to new quiet option for our production boot :-)

I also saw "Extract BCD, boot.sdi, and standard boot font files automatically from the .wim image." in the 2.7.1 release notes, which sounds like an awesome addition. But, I am not sure how to take advantage of that or if I've encountered a bug. I tried simply commenting out my initrd lines in my iPXE menu with the BCD and boot.sdi files, but WinPE fails to load with an error saying that the BCD file is unavailable (photo attached).

Is this a bug, or do I need to supply some argument to wimboot for it extract these files automatically? I double checked that the BCD and boot.sdi files are within \Windows\Boot\DVD\[EFI & PCAT]\ in my wim file.

IMG_1067

Add .mui

I found some systems that require a MUI file to display the Bootmgr interface

  • \efi\boot\microsoft\boot\en-us\bootmgfw.efi.mui
  • \boot\en-us\bootmgr.exe.mui

These folders are on the same level as the Fonts folder. Hope this gets implemented.

Thanks.

Wimboot hanging at bootmanager saying "unexpected error"

I extracted all my files from a windows 10x64 iso and running it on such a system in a vm using a custom build ipxe iso.

After i loaded the files and bootet (as can be seen in this video (sorry for the slow typing i thought a video would be helping more than a screenshot)) the windows bootloader hangs saying unexpected error "status: 0xc0000225"

Adding "index=2" doesnt help.

Thanks in advance and sorry for my english

Ability to disable debug output

Description

As a user, I would like the ability to disable debug output as a runtime choice, so that I can retain a clean user interface appearance unless I actually need to see the debug output.

Acceptance criteria

  • Debug output appears as at present in standard build
  • Debug output may be inhibited by adding quiet to the wimboot command line

Directories are patched into the WIM as zero sized files

I tried to use wimboot with GRUB to boot a WinPE image in a UEFI environment. In efi_extract everything is enumerated from the root directory and not just the files.

In my case this meant that the boot (where GRUB installs itself) and efi directories from the root directory of the EFI system partition were patched into the WIM file.

Using boot via 0xba9a54d8 len 0x0
Using efi via 0xba9a5598 len 0x0

...patching WIM boot at [0xff4d44d,0xff4d44d)
...patching WIM efi at [0xff4d44d,0xff4d44d)

This causes the boot process to fail since \windows\system32\boot\winload.efi won't be usable anymore.
The issue can be fixed by skipping the directory entries, with this change I could boot successfully.

diff --git a/src/efifile.c b/src/efifile.c
index a7a9cf3..96377af 100644
--- a/src/efifile.c
+++ b/src/efifile.c
@@ -185,6 +185,11 @@ void efi_extract ( EFI_HANDLE handle ) {
 		if ( size == 0 )
 			break;
 
+		/* Skip directories */
+		if ( info.file.Attribute & EFI_FILE_DIRECTORY ) {
+			continue;
+		}
+
 		/* Sanity check */
 		if ( idx >= VDISK_MAX_FILES )
 			die ( "Too many files\n" );

Feel free to use this and commit it under your name.

XPRESS compression support

As a user, I would like to be able to use WIM images that happen to be compressed using the XPRESS scheme (as an alternative to the already-supported LZX scheme), so that I don't have to worry about which compression scheme my image is using.

Support to SWM Files

Are there plans to add support to SWM files?

Like:

kernel wimboot
initrd  ipxe/BCD  BCD
initrd  ipxe/boot.sdi  boot.sdi
initrd ipxe/boot.swm boot.swm
initrd ipxe/boot2.swm boot2.swm
initrd ipxe/boot3.swm boot3.swm
boot

Thanks!

Breaks when using LTO

Our Arch Linux CI targets nehalem+ and uses LTO by default to build pacakges, including the AUR wimboot-git package.
Since we switched to using it by default, the package started to fail builds with:

...
[2021-01-31T03:09:58.227Z] cc -march=haswell -mtune=intel -O2 -pipe -fno-plt -flto=8 -Os -ffreestanding -Wall -W -Werror -nostdinc -I. -fshort-wchar -DVERSION="\"v2.6.0\"" -fno-stack-protector -fno-dwarf2-cfi-asm -fno-exceptions  -fno-unwind-tables -fno-asynchronous-unwind-tables  -Wno-address-of-packed-member  -include compiler.h -m32 -march=i386 -malign-double -fno-pic -S sha1.c -o sha1.i386.s
[2021-01-31T03:09:58.227Z] as --64 i386.i sha1.i386.s -o sha1.i386.x86_64.raw.o
[2021-01-31T03:09:58.227Z] objcopy --prefix-symbols=__i386_ sha1.i386.x86_64.raw.o sha1.i386.x86_64.o
[2021-01-31T03:09:58.227Z] rm -f lib.x86_64.a
[2021-01-31T03:09:58.227Z] ar r lib.x86_64.a prefix.x86_64.o startup.x86_64.o callback.x86_64.o main.x86_64.o vsprintf.x86_64.o string.x86_64.o peloader.x86_64.o int13.x86_64.o vdisk.x86_64.o cpio.x86_64.o stdio.x86_64.o lznt1.x86_64.o xca.x86_64.o die.x86_64.o efi.x86_64.o efimain.x86_64.o efiguid.x86_64.o efifile.x86_64.o efipath.x86_64.o efiboot.x86_64.o efiblock.x86_64.o cmdline.x86_64.o wimpatch.x86_64.o huffman.x86_64.o lzx.x86_64.o wim.x86_64.o wimfile.x86_64.o pause.x86_64.o sha1.x86_64.o prefix.i386.x86_64.o startup.i386.x86_64.o callback.i386.x86_64.o main.i386.x86_64.o vsprintf.i386.x86_64.o string.i386.x86_64.o peloader.i386.x86_64.o int13.i386.x86_64.o vdisk.i386.x86_64.o cpio.i386.x86_64.o stdio.i386.x86_64.o lznt1.i386.x86_64.o xca.i386.x86_64.o die.i386.x86_64.o efi.i386.x86_64.o efimain.i386.x86_64.o efiguid.i386.x86_64.o efifile.i386.x86_64.o efipath.i386.x86_64.o efiboot.i386.x86_64.o efiblock.i386.x86_64.o cmdline.i386.x86_64.o wimpatch.i386.x86_64.o huffman.i386.x86_64.o lzx.i386.x86_64.o wim.i386.x86_64.o wimfile.i386.x86_64.o pause.i386.x86_64.o sha1.i386.x86_64.o
[2021-01-31T03:09:58.227Z] ar: creating lib.x86_64.a
[2021-01-31T03:09:58.227Z] ranlib lib.x86_64.a
[2021-01-31T03:09:58.227Z] cc -Wall -W -Werror -I/usr/include -I/usr/include -I/usr/include -idirafter . efireloc.c -L/usr/lib -L/usr/lib -L/usr/lib -lbfd -ldl -liberty -lz -Wl,--no-warn-search-mismatch -o efireloc
[2021-01-31T03:09:58.227Z] ld -m elf_x86_64 -T script.lds -o wimboot.x86_64.elf -q -Map wimboot.x86_64.map \
[2021-01-31T03:09:58.227Z] 	prefix.x86_64.o lib.x86_64.a
[2021-01-31T03:09:58.227Z] ld: lib.x86_64.a(efimain.x86_64.o): plugin needed to handle lto object
[2021-01-31T03:09:58.227Z] ld: lib.x86_64.a(efimain.x86_64.o): plugin needed to handle lto object
[2021-01-31T03:09:58.227Z] ld: lib.x86_64.a(efimain.i386.x86_64.o): plugin needed to handle lto object
[2021-01-31T03:09:58.227Z] ld: prefix.x86_64.o: in function `opt_header':
[2021-01-31T03:09:58.227Z] (.prefix+0x68): undefined reference to `efi_main'
[2021-01-31T03:09:58.227Z] (.prefix+0x68): relocation truncated to fit: R_X86_64_32 against undefined symbol `efi_main'
[2021-01-31T03:09:58.227Z] ld: prefix.x86_64.o: in function `setup':
[2021-01-31T03:09:58.227Z] (.prefix+0x2a7): undefined reference to `__i386_cmdline'
[2021-01-31T03:09:58.227Z] ld: (.prefix+0x2b2): undefined reference to `__i386_initrd'
[2021-01-31T03:09:58.227Z] ld: (.prefix+0x2bd): undefined reference to `__i386_initrd_len'
[2021-01-31T03:09:58.227Z] ld: lib.x86_64.a(startup.i386.x86_64.o): in function `__i386_startup':
[2021-01-31T03:09:58.227Z] (.text+0x29): undefined reference to `__i386_main'
[2021-01-31T03:09:58.227Z] make: *** [Makefile:89: wimboot.x86_64.elf] Error 1
[2021-01-31T03:09:58.227Z] rm lznt1.x86_64.s prefix.i386.s main.i386.x86_64.raw.o callback.i386.x86_64.raw.o huffman.x86_64.s cmdline.x86_64.s sha1.i386.s efipath.x86_64.s efimain.i386.x86_64.raw.o vdisk.i386.s efiboot.x86_64.s huffman.i386.s main.x86_64.s efiguid.x86_64.s efifile.i386.x86_64.raw.o efipath.i386.s efiboot.i386.x86_64.raw.o lzx.i386.x86_64.raw.o wimfile.i386.x86_64.raw.o int13.i386.s xca.i386.s efimain.x86_64.s string.i386.x86_64.raw.o peloader.i386.s efiblock.i386.s cpio.x86_64.s wim.i386.x86_64.raw.o peloader.x86_64.s efipath.i386.x86_64.raw.o pause.i386.s efifile.x86_64.s die.i386.s peloader.i386.x86_64.raw.o startup.i386.s stdio.x86_64.s vsprintf.i386.x86_64.raw.o wimpatch.i386.x86_64.raw.o huffman.i386.x86_64.raw.o efiblock.i386.x86_64.raw.o efiboot.i386.s int13.i386.x86_64.raw.o main.i386.s cpio.i386.x86_64.raw.o lznt1.i386.x86_64.raw.o wimpatch.x86_64.s sha1.x86_64.s die.i386.x86_64.raw.o stdio.i386.s startup.i386.x86_64.raw.o efiguid.i386.x86_64.raw.o efiblock.x86_64.s vsprintf.i386.s xca.x86_64.s wim.x86_64.s sha1.i386.x86_64.raw.o pause.x86_64.s efifile.i386.s prefix.i386.x86_64.raw.o efi.i386.x86_64.raw.o vdisk.x86_64.s callback.i386.s vdisk.i386.x86_64.raw.o callback.x86_64.s efimain.i386.s string.x86_64.s wim.i386.s cmdline.i386.x86_64.raw.o efi.i386.s efiguid.i386.s lzx.i386.s vsprintf.x86_64.s lznt1.i386.s wimfile.x86_64.s wimpatch.i386.s startup.x86_64.s prefix.x86_64.s int13.x86_64.s efi.x86_64.s pause.i386.x86_64.raw.o cpio.i386.s cmdline.i386.s string.i386.s wimfile.i386.s die.x86_64.s xca.i386.x86_64.raw.o stdio.i386.x86_64.raw.o lzx.x86_64.s
[2021-01-31T03:09:58.227Z] ==> ERROR: A failure occurred in build().
[2021-01-31T03:09:58.227Z]     Aborting...

Dropping the LTO build flags fixes this.
Is this an actual bug or an intrinsic property required to function across 32 & 64 bit which breaks the LTO build? In practice - should we fork the AUR package and inject our LTO-disable function into the build function, or is this something that might be fixed upstream and we can just re-enable in our CI later?

ARM32/ARM64 builds

Description

As a user I would like to be able to use wimboot on ARM platforms, so that I can boot Windows on ARM platforms using HTTP.

Acceptance criteria

  • Ability to build wimboot as either a 32-bit or 64-bit ARM UEFI executable
  • Ability to invoke the ARM wimboot executable via qemu+OVMF+iPXE

Dependencies

Does not boot on EFI syslinux

Hello, this is the config I'm using to load Windows 10 install media via syslinux with wimboot:

LABEL windows
    KERNEL linux.c32
    APPEND wimboot initrdfile=/boot/bcd,/boot/boot.sdi,/sources/boot.wim

It works perfectly on BIOS syslinux, but on EFI syslinux, after it finishes "encapsulating" all the files, it simply reboots.

Does @bootmgr=/bootmgr.exe work under grub4dos?

Some WinPE's do not contain bootmgr.exe (they have no \windows\boot\PXE folder).
I tried using @BOOTMGR= but it seems to be ignored.
I have tried several versions of wimboot.

initrd @startupe2b.bat=%st% @winpeshl.ini=/_ISO/e2b/grub/winpeshl.ini @bcd=(0xff)%pp%/boot/bcd @bootmgr=(0xff)/bootmgr.exe @boot.sdi=(0xff)%pp%/boot/boot.sdi @boot.wim=(0xff)%pp%/sources/boot.wim %redir%

This works OK if the wim file contains PXE\bootmgr.exe, but does not work with those boot.wim files that don't contain that file. The file \bootmgr and \bootmgr.exe are present in the ISO so @BOOTMGR=(0xff)/bootmgr.exe should work.

Problem with bootmgr.exe

Wimboot version 2.7.3
Trying boot WinPE via iPXE and get error “FATAL: no bootmgr.exe”
027FF126-0DE2-49C5-96DF-89985F208595

If i place bootmgr.exe extracted from boot.wim directly and define it with “ imgfetch --name bootmgr.exe bootmgr.exe bootmgr.exe” in ipxe script - i get error: “Bad MZ magic 0000” and “FATAL: Could not load bootmgr.exe”
9ED31D3A-DE4E-4073-B887-B1467E143386

Wimboot specifying wim index not working in version 2.7.4

Discussed in #40

Originally posted by rushyn January 20, 2023
Has any one else run in to an issue after updating to last wimboot(2.7.4) where index= (number) no longer works, and instead it boots the first index of the wim file? Updated from wimboot 2.5.2 going back to 2.5.2 resumes normal behavior.

wimboot Bad CPIO Magic FATAL: could not extract inited file

I work at university and ipxe server is used a lot.

I am using the latest version of wimboot.

When a request is made in bios mode on my ipxe server, I encounter an error as in the picture.

It boots fine in efi mode.

I also used old version wimboot files and got the same error.

I searched a lot on the internet but none of the solution suggestions worked.

I hope you can find a solution.

Here is the code in my boot.ipxe file:

`#!ipxe
set boot-url http://myipxeserver.com/
cpuid --ext 29 && set arch x86_64 || set arch i386 ; echo CPU Arch: ${platform} ${arch}
goto ${platform}

:efi
set path_file /WinPE/Acronis/boot.wim
kernel ${boot-url}/Tools/MS/pxe/wimboot
initrd ${boot-url}/Tools/MS/pxe/efi/microsoft/boot/bcd
initrd ${boot-url}/Tools/MS/boot.sdi
initrd ${boot-url}${path_file}
boot || goto failed

:pcbios
set path_file /WinPE/Acronis/boot.wim
kernel ${boot-url}/Tools/MS/pxe/wimboot rawbcd
initrd ${boot-url}/Tools/MS/pxe/boot/bcd bcd
initrd ${boot-url}/Tools/MS/boot.sdi boot.sdi
initrd ${boot-url}${path_file}
boot || goto failed

:failed
chain --autofree http://myipxeserver.com//menu.ipxe`

wimboot_error

memmap_next might cause an infinite loop

I used iPXE with wimboot to boot wim images, the configuration works well except on an old machine, whose components could mainly dateback to 2004 and 2005, BIOS only. It hangs after prompting "Emulating drive 0x81", shown as the figure below.

Hangs after emulating drive

After compilation of wimboot with higher verbose level, I found that it enters an infinite loop, and the message starting with "INT 15,e820" points to the memmap.c section. Tracing the program, the chain is

main.c(main)->paging.c(relocate_memory_high)->memmap.c(memmap_next)

I limited the maximum iteration in paging.c, and added some details in verbose messages of memmap.c:

...
		DBG2 ( "INT 15,e820 region [%llx,%llx) type %d attr %d next=%d\n",
		       e820_buf.start, ( e820_buf.start + e820_buf.len ),
		       e820_buf.type, e820_buf.attrs, e820_ebx);

		/* Skip non-RAM regions */
		if ( e820_buf.type != E820_TYPE_RAM ) {
			continue;
		}
		if ( params.ecx > offsetof ( typeof ( e820_buf ), attrs ) ) {
			if ( ! ( e820_buf.attrs & E820_ATTR_ENABLED ) )
				continue;
			if ( e820_buf.attrs & E820_ATTR_NONVOLATILE )
				continue;
		}
		DBG2 ( "^^ RETURNED AS E820_BUF\n" );
		/* Return this region */
		return &e820_buf;

	} while ( e820_ebx != 0 );

	DBG2 ( "NULL WILL BE RETURNED\n" );
	return NULL;

IMG_20210221_020310

And compare with a virtual machine messages:

Screenshot_2021-02-21_02-08-38

I can now confirm that there is some bug in memmap.c: as long as the last entry (where [ebx(next) = 0]) returns, then the caller of memmap_next (usually use another while to call) enters an infinite loop.

A noticeable difference of the two figures above is about the map order. In the virtual machine, these maps are sorted, and the last entry will be ignored because it is probably reserved, then e820_ebx != 0 is checked, so memmap_next returns NULL; but in my old machine, these maps are not sorted by address offset, i.e. the last entry is possible to be any type, then it might be returned by memmap_next. Then it causes an infinite loop in paging.c(relocate_memory_high)

while ( ( e820 = memmap_next ( e820 ) ) != NULL ) {

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.