Git Product home page Git Product logo

q5.js's People

Contributors

datkat21 avatar ormaq avatar quinton-ashley avatar tezumie 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

Watchers

 avatar  avatar  avatar  avatar

q5.js's Issues

pixel fonts not rendered properly

Yeah... this is a bit of a rabbit hole! 🐰

q5.js currently can't render pixel fonts (very small sized fonts made of just a few pixels) at a pixel perfect level (sharp and blocky) because q5 uses native canvas rendering. Every browser's canvas.fillText implementation seems to do anti-aliasing and the ctx.textRendering property doesn't seem to change that, at least not in Chromium based browsers. Also Safari does not have this feature. https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/textRendering

p5.js can sometimes render pixel fonts correctly, if the font is the right size and at a correct position (not always integer coordinates!). p5 does not use native canvas rendering for true type and open type fonts, it uses OpenType rendering. My guess is that this decision was made because around the time p5.js was created, browser's native canvas implementations for font rendering were not so good. Nowadays though, in any other case besides pixel fonts, q5's native canvas rendering with canvas.fillText definitely looks better and probably runs faster than p5's OpenType based method. Which I noted here:
https://discourse.processing.org/t/why-is-text-not-centered-properly-with-textalign-center-center/43322/8

However, in the case of pixel fonts, using OpenType to render text as primitive canvas shapes actually works great because pixel fonts are just made out of a few pixels (squares). So OpenType rendering could be used for pixel fonts and native for everything else? Well things are not so simple.

Take a look at this demo with a font called Bit Potion, the creator says it's best to use it in 16px intervals. I made this demo which uses p5.js and shows text displayed with the pixel font at 16px and 32px.

https://editor.p5js.org/quinton-ashley/sketches/fhr7cLd3R

Clicking the mouse in the demo will switch the text from being rendered at integer coordinates to half pixel coordinates (0.5). For some reason, at any even multiple of 16px, such as 32px, text only looks good at integer coordinates. At any odd multiple of 16 such as 48px or 16px, text only looks good at half pixel coordinates and only without letters that hang under the baseline like "p".

Why is that? Well I found this old stack overflow post where a user says "the canvas coordinate system defines the start of a pixel from the pixel's center" https://stackoverflow.com/questions/22976031/html5-canvas-without-anti-aliasing?rq=3

This sucks for users. Displaying pixel fonts shouldn't be a difficult guessing game. Users should just be able to put in any random coordinates and as long as the font is the right size, q5 should be able to draw it with pixel precision (sharpness).

But how could such a generalized solution be achieved? I don't know yet.

One idea is to add a feature to p5 and q5 where users could define the optimal size of a font. Then when the text function was used with the font, there could be some code that could adjust the position given by the user to make the font display well.

Another totally differnt idea I had was that p5play users could import a pixel font as a spritesheet animation, provided the font offer an image containing all the font's symbols. Then text could be displayed using the Tiles constructor. However, this has many obvious limitations. It'd only work for monospace fonts, not for the "Bit Potion" font. Also this method is untenable for non-English text with accented letters. Also it'd make using the Tiles constructor for anything else pretty much impossible. Yet, this approach could be decent for English text as long as it was implemented as a separate system to not conflict with p5play's Tiles system.

But I also noticed the Bit Potion font is offered as an old bitmap .fnt windows font file. I wonder if there's any canvas library for rendering them? This could be a good solution. I will research this more.

async `load` function

An async function simply named load could be used to load any file or multiple files asynchronously and return a promise.

This will be implemented in q5-util.js The q5 setup function is already async.

This idea was suggested by @mattdesl and iterated on by @mvicky2592 in this p5.js issue discussion. I also think keeping the preload system for backwards compatibility and ease of use for beginners is the right idea though.
processing/p5.js#6767

colorMode

Hello. I try to test q5.js in replacement of p5.js for a website (only some small colors image conversion).
On p5js, I manage to do it with a HSL colorMode.

On q5js, there are only RGB mode and HSB mode. It is true?

Bug with transparency in sprites in version 1.9x

Code:

var player;

function preload() { 
  new Canvas("2:5"); 
   
  player = new Sprite(100,100);  
  //player.spriteSheet = loadImage("questKid.png");
  player.spriteSheet = "https://p5play.org/learn/assets/questKid.png";
  player.w = 32;
  player.h = 32;    
}

function setup() {	 
  background("blue"); 

  player.addAnis(
    {
      right:{row:0, frames: 8},
      left:{row:1, frames: 6},      
      down:{row:2, frames: 5},
      up:{row:3, frames: 7},      
      kuv:{row:4, frames: 8},      
    }
  )    
}

function draw() {
  clear();
  background("blue"); 

	if (kb.presses("r")) player.changeAni("right");
	if (kb.presses("j")) player.changeAni("left");
	if (kb.presses("l")) player.changeAni("down");
	if (kb.presses("t")) player.changeAni("up");
	if (kb.presses("s")) player.changeAni("kuv");  
}

If you use version q5.js 1.9.9:
sprite

If you use version q5.js 1.8.7:
sprite2

Basic WebGPU rendering

The devs of the WebGL renderer for p5.js, namely Dave Pagurek, have done a great job with it. I have no intention of reinventing the wheel and recreating webgl mode in q5.

WebGPU is the latest and greatest graphics API for the web, built on the low-level GPU APIs Metal and Vulkan.

I've seen some impressive demos of it, like this one that can draw millions of boids!
https://jtsorlinis.github.io/BoidsWebGPU/

I expect Apple to release WebGPU support for iOS in September. Currently this feature is only available for beta testing in Safari Technology Preview. I would love for q5 to have basic WebGPU rendering support before then and ideally by the end of July.

Basic WebGPU support in q5 will consist of drawing shapes and images in two dimensions. Although this can already be done in Babylon.js, it's not as lightweight or as easy to use as I hope q5's implementation will be.

Once all that is complete, more complex rendering functionality like support for text, can be implemented.

Basic 3D drawing would be nice too but personally I'm not going to attempt competing with Babylon.js on providing more advanced 3D capabilities.

`curveTightness` behaviour

Co-author @lingdong~ wrote:

curveTightness() sets the 'alpha' parameter of Catmull-Rom curve, and is NOT identical to p5.js counterpart. As this might change in the future, please call curveAlpha() directly.

`preload` and `async setup` loading animation

@SableRaf I like this idea!

processing/p5.js#6795

I did something similar with p5play with the "made with p5play" screen but it fills the window, assuming nearly all usage of p5play is in global mode.

For q5, I'm thinking of doing a loading animation of the q5 logo spinning in css right above the center of the canvas.

In response to the opt-in idea, I think that wouldn't work because developers loading assets locally may never experience long loading times, so they wouldn't see the message suggesting to enable it. Also my assumption is a loading animation would always be preferable to "loading" text.

Refactor `color` and add support for oklch

I want to refactor Q5.Color and the color function to add support for HDR colors, make oklch a new color mode, and remove HSB/HSV support.

p5 devs are planning to improve how color is handled in p5.js v2. I'd like them to take a look at my goals for the color improvements I will make in q5 v1.9.3. Hopefully some of these changes can be made in p5 as well!
processing/p5.js#6680

@limzykenneth @lindapaiste @Vishal2002 @JustZambetti @davepagurek @hellonearthis @meodai

Maintain good performance

q5 is 11x faster than p5 at creating 10,000 random colors: 3ms vs 33ms.

function randomColors() {
    for (let i = 0; i < 100000; i++) {
        let c = color(random(255), random(255), random(255));
    }
}

const start = performance.now();

randomColors();

const end = performance.now();
console.log(`Execution time: ${end - start} ms`);

What kind of magic is q5 using to achieve such results? q5 simply doesn't do any unnecessary color format conversions. Since the default color mode is RGB, q5 simply stores the user's rgb values as properties in a object.

But in p5, the user's legacy rgb 0-255 range input is converted to modern 0-1 range rgba values, both of which are stored in arrays. maxes arrays for each color format are also copied into every color object, which seems unnecessary. hsba and hsla are both defined on the object as null, even if the user doesn't intended to convert the color.

Make color components easier to view and edit

If you view a p5.Color in the console, you'll probably be confused by the output! Especially if you're a beginner programmer.

{
	hsba: null,
	hsla: null,
	levels: [255, 10, 82, 255],
	maxes: {
	  hsb:  [360, 100, 100, 1],
	  hsl: [360, 100, 100, 1],
	  rgb: [255, 255, 255, 255]
	},
	mode: 'rgb',
	_array: [1, 0.0392156862745098, 0.3215686274509804, 1]
}

To try to understand why p5.Color was implemented this way, we have to study its grandaddy: Processing.

In Java Processing, the color method generated a primitive int, not an Object, and in Java ints can't have properties or methods. That's why the red, green, blue, alpha functions were necessary. Those individual color components could only be retrieved from a color int through bitwise operations, not via properties.
https://forum.processing.org/two/discussion/15923/how-to-change-alpha-without-changing-the-whole-color.html

But in p5 these functions should've only been implemented for backwards compatibility. Thankfully color.setRed() and similar functions were introduced, but why not a color.getRed() or better yet expose the color components as public properties?

To improve q5 I'm taking inspiration from the popular JS color library culori represents colors as objects with letter properties, using a single color mode per object, making them easy to view and edit.

// culori.js rgba color data representation
{
  r: 255,
  g: 10,
  b: 82,
  a: 1,
  mode: 'rgba'
}

oklch support

But just making red, green, and blue components easier to edit doesn't fix the more fundamental problems with the RGB format. As noted in the article "OKLCH in CSS: why we moved from RGB and HSL":

"RGB, hex and color(display-p3) aren’t convenient for color modifications because, for the vast majority of humans, it’s difficult to intuitively set colors by changing the amount of red, blue and green. Further, RGB and hex also can’t encode P3 colors."

I recommend reading the full article, it's great! I'm going to make separate classes for rgb and oklch that extend Q5.Color, so that instanceof p5.Color checks can still work.

Currently Q5.Color instances internally output color to ctx.fillStyle or ctx.strokeStyle via their toString method. For Q5.ColorRGBA and Q5.ColorOKLCH their toString() functions won't require any calculations.

Auto-upgrade RGB to HDR

While I do think oklch is the future, I don't really expect any Intro to Web Design class/section to teach oklch anytime soon. But I want to get people using HDR colors today!

I think the best way to do this is to create a backwards compatibility class, Q5.ColorRGBA_P3. Users can still edit colors using the default RGB colorMode but the resulting colors will be internally mapped directly to the HDR "display-p3" color space in toString using this format: color(display-p3 r g b / a).
https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color

To prevent toString from needing to do the format conversion from the legacy 0-255 range to the modern 0-1 range whenever toString is used, I'll create getter/setter properties for the purpose of watching for changes. Only when the color is changed will toString do new calculations.

Let users make colors using new Color()

p5.Color is not a class that belongs to a p5 instance but rather p5 itself. q5 mimics this implementation.

Yet,_colorMode is an instance variable, so it can't be accessed directly by the p5.Color constructor. p5 gets around it by passing p5 instances to the p5.Color constructor. Additionally, in q5 I'll need to access the canvas to get the canvas' color space. In q5, all of the color parsing code is done in the q5 instance level color function. I will maintain this structure.

Depending on the instance's current color mode and canvas color space, the q5 instance variable $.Color will be set to Q5.ColorRGBA, Q5.ColorRGBA_P3, or Q5.ColorOKLCH. These constructors will have no parsing overhead.

Remove HSB/HSV support

Support for the HSV color format will be removed in q5 v1.9.3 because it was rarely used, the amount of code required to convert between RGB and HSV was quite large, and color experts considered HSV to be flawed and outdated way back in 1997!

https://en.wikipedia.org/wiki/HSL_and_HSV#Disadvantages

normalize() doesn't return the same as p5

I've been testing the following sketch. I've debugged al the way until normalize() (line 80), which results in a NaN. If I comment out the normalize it starts working for a couple of frames but runs in to more issues.

Stopped debugging here but this might be a good test case to find some more discrepancies

Make q5 modular! v2.0 release

I've broken q5 up into the smallest useful modules which are in the src folder. This is great for organizational purposes and also for extremely lightweight use in which users only need to load the modules necessary for a particular sketch to run.

Modules will load via side effects to keep things simple for students.

Implement `sound.setPan`

My goal is for q5.js to have basic sound support so users don't need to load p5.sound if they don't need to. See loudSound for the current implementation.

I think setPan would be a great addition but I couldn't yet figure out how to do it. If anyone wants to implement this let me know!

Sync instance props to global scope using a Proxy

I was wrong about getters and setters being a good way to sync properties between q5 instances and the global scope.

if (scope == 'global') {
    let props = Object.getOwnPropertyNames($);
    let t = !Q5._nodejs ? window : global;
    for (let p of props) {
        if (typeof $[p] == 'function') t[p] = $[p];
        else {
            Object.defineProperty(t, p, {
                get: () => $[p],
                set: (v) => ($[p] = v)
            });
        }
    }
}

That's because calling a getter function is much less efficient than simply accessing the value of a prop.

Since q5 properties can commonly be accessed in for loops in the draw loop, significant slowdowns could occur.

p5 solve the problem this way.

this._setProperty = function (prop, value) {
  _this[prop] = value;
  if (_this._isGlobal) {
    window[prop] = value;
  }
};

Any time (over 100 times) that one of the synced props is set, the function must be used.

this._setProperty('mouseIsPressed', true);

This is a bit cumbersome and doing it this way may have increased q5's overall size by a kb.

$.mouseIsPressed = true;
// would need to change to
$._setProp('mouseIsPressed', true);

Instead I used a proxy.

let p = new Proxy($, {
  set: (t, p, v) => {
    $[p] = v;
    if ($._isGlobal) globalScope[p] = v;
    return true;
  }
});

Proxies are nice because they can be used just like objects.

p.mouseIsPressed = true;

Implemented in v2.0-beta12

These changes actually resulted in a slight decrease of q5's overall size!

heading()

From the flocking p5 example

this.velocity = p.createVector(p.random(-1, 1), p.random(-1, 1));

let theta = this.velocity.heading() + p.radians(90);

Upon calling this code we get that $ is not defined

you can fix this using

heading() {
		let $ = this;
		return Math.atan2($.y, $.x);
	}

but this is not a pretty solution * And does not really work*

Friendly Error System module

Fork the Friendly Error System from p5.js and offer it as an optional q5 module.

p5's FES explain errors in a friendly way. Though beginners can consult AI to get more detailed explanations of errors, sometimes a hint from FES would be enough to guide them to think of a solution.

tinting is broken

Somehow I broke tinting in the latest version of q5. Will fix ASAP!

Batch Rendering in WebGPU

There's an opportunity with q5's upcoming WebGPU renderer to implement friendly p5 style wrappers for batch rendering.

In p5.js every function that draws stuff (like rect and image) is run sequentially and layers stuff on top of previously drawn items. This doesn't take advantage of the GPUs strength at drawing in parallel which is faster.

Batch rendering could draw many shapes with the same fill and stroke colors or shader in the same render pass.

Proposed use example:

fill("red");

batchStart();

for (let i = 0; i < 100; i++) {
  rect(i*10, random(height), 10, 10);
}

batchEnd();

In between the start and end of a batch, drawing functions like rect would add vertices to a vertex buffer behind the scenes.

I hope this will give users an easy and performant way to render millions of 2D shapes.

vec.normalize() is incorrect if values have changed

Looks like q5's optimizations don't account for changes to a vector's components.

var vec = new p5.Vector();

vec.x = 2; 
vec.normalize();
console.log(vec.x); // gives 1

vec.y = 1;
vec.normalize();
console.log(vec.x) // gives 0.707 in p5, but still 1 in q5

Set canvas context attributes

Motivation:

Users should be able to set canvas context attributes.

p5.js currently doesn't expose the ability to change 2d canvas context attributes. They can only be changed when the canvas is created and getContext is used for the first time.

I'm especially interested in being able to set new defaults for q5.js:

Q5.canvasOptions = {
	alpha: false,
	desynchronized: true,
	colorSpace: 'display-p3'
};

alpha: most sketches do not take advantage of the canvas element's transparency and disabling it would improve performance

desynchronized: most sketches would benefit from desynchronization with the DOM, unless the sketch used DOM elements above the canvas.
https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext
https://developer.chrome.com/blog/desynchronized

colorSpace: If the display supports "display-p3" I want q5 to use it. #21

Related issue:
processing/p5.js#5902

Implementation:

This could be done via a createCanvas input parameter. For example:

createCanvas(800, 600, '2d', {
  alpha: false
}); 

Users could also change the defaults held in Q5.canvasOptions which would have an effect on all instances of q5. I will need to do this on p5play.org because I actually do take advantage of the canvas's alpha via the clear function which makes light and dark mode possible on the site without needing to change the example code.

Tint caching

Looking over the code at school at the moment- tinted images seem to create their offscreen canvas, apply filters, then draw- every frame. If the tint stays the same, it makes sense to cache the canvas with the image, for quick drawing for subsequent frames. This would also help with changing tints, as they could simply change the pre-existing canvas without the need for overhead creating more canvases.

q5-dom.js implementation

p5 API for dom functions should be implemented as a supplementary module, q5-dom, that won't be included in the default bundle.

I think the p5 dom api could be improved by making it easier for beginners to use CSS flexbox to create better looking UIs with html+css that could overlay the canvas.

If anyone would like to implement this lmk!

ColorMode ('oklch') doesn't seem to be working properly.

I found your project while I was considering whether I should access the canvas directly because p5.js didn't support color space including oklch. To see if it supports wide gamut, I tried to compare the color of p3 color gamut with its fallback through the code below.

function setup() {
  createCanvas(400, 400);
  background(220);
  colorMode('rgb');
  let colourRgb = '#ff0c52';
  colorMode('oklch');
  let colourOklch = color(0.637, 0.28, 16.57, 1);
  fill(colourRgb);
  rect(0, 0, width / 2, height);
  fill(colourOklch);
  rect(width / 2, 0, width / 2, height);
}

However, the error phrase below appeared on the console.

TypeError: Cannot read properties of undefined (reading '_q5Color')
    at $.fill (q5.js:642:11)
    at setup (sketch.js:10:3)
    at Q5.$.<computed> [as setup] (q5.js:209:17)
    at _start (q5.js:226:11)
    at new Q5 (q5.js:236:3)
    at HTMLDocument.<anonymous> (q5.js:270:23)

I think the fill function cannot accepts color data created through colorMode('oklch') as a variable, do I have to use a different method when using colorMode('oklch') or is it a feature that hasn't been implemented yet, or is it a bug?
By the way, #ff0c52 appeared pinkish red but little bit saturated as intended, and oklch color was rendered...... gray.

`displayMode`

@Tezumie Let me know what you think of the displayMode function. I put it in a separate module since the code is 2kb and it can work with the webgpu canvas too in the future.

displayMode(mode, renderQuality, displayScale)

It supports three display modes: "normal": no styling to canvas or its parent element
"centered": canvas will be centered horizontally and vertically within its parent and if it's display size is bigger than its parent it will not clip
"maxed": canvas will fill the parent element, same as fullscreen for a global mode canvas inside a main element
"fullscreen": canvas will fill the screen with letterboxing to persevere its aspect ratio, like css object-fit contain

two render qualities:
"default": pixelDensity set to displayDensity
"pixelated": pixelDensity set to 1 and various css styles are applied to the canvas to make it render without image smoothing

displayScale can be given as a string "x2" or a number. This can be used to make small canvases appear larger.

CreateP is not yet available ?

I love your library! I tested it with some of my sketches but notice that some functions are still missing, like createP. Or am I missing something?

`resizeCanvas` fails on mobile device orientation change

Here's the example using p5play that isn't working.

Strangely this issue only occurs in Safari and not in the iOS app template that I'm working on.

I can't figure out what's wrong compared to p5's implementation.

let boxes = {};

function setup() {
	new Canvas();
}

function draw() {
	background(16);

	renderStats();

	textSize(24);
	textAlign(CENTER);
	fill(200);
	text('Tap to create a new sprite', canvas.hw, canvas.hh);

	let info = canvas.w + 'x' + canvas.h + ' X' + pixelDensity() + ' ' + deviceOrientation;
	text(info, canvas.hw, canvas.hh + 30);

	for (let touch of touches) {
		if (touch.presses()) {
			let box = new Sprite(touch.x, touch.y, 30, 30);
			boxes[touch.id] = box;
		} else {
			boxes[touch.id].moveTowards(touch);
		}
	}
}

function windowResized() {
	canvas.resize();
}

Support for HDR displays

Motivation:

I want users to be able to use more vibrant HDR colors in their q5.js projects.

Check out my HDR web design guide for more info:
https://quinton-ashley.github.io/hdr-web-design-guide/

HTML5 canvas contexts can use the "display-p3" HDR color space but p5.js doesn't support it.
https://developer.mozilla.org/en-US/docs/Web/API/ImageData/colorSpace

https://webkit.org/blog/12058/wide-gamut-2d-graphics-using-html-canvas/

Implementation:

I want to make "display-p3" the default color space in q5.js, as long as the user's device is capable. Most devices made after 2017 are!
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Testing_media_queries

A canvas' color space can be set via canvas context attributes #23

But properly supporting HDR isn't as simple as just changing the color space. I also need to give users a way to use HDR colors. In CSS the format for hex rgba, legacy rgba, srgb, and display-p3 are all different.

I think q5's color system needs to be rewritten #24

multi-line text not vertically centered with textAlign

function setup() {
	new Canvas(400, 400);
  displayMode("maxed", "pixelated");
	background(220);
}

function draw() {
	background(220);

	translate(canvas.hw, canvas.hh);
	textAlign(CENTER, CENTER);
	textSize(100);
	text('hello\nbye', 0, 0);
}

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.