Pixel Canvas [WinWorld]🎨 INTRO
Hey, everyone!
Today we would like to present a tool that is unusual by TradingView's standards. This is not a conventional trading indicator, not a strategy, not a fancy tracker or yet another "AI-powered cash printer". This indicator was built purely for fun from our own little ideas, which we code from time to time for ourselves.
Even though we usually keep most such "products" to ourselves because they are too niche, so to speak, we decided to share this one with everyone, because we think the community might actually enjoy this one for the very simple reason – it lets you place images on your chart.
This kind of idea was implemented partly in "Pixel Art" indicator by @pAulseperformance , where you can choose different pixel arts from pre-loaded options.
It looks fine, but:
what if we want to load our own pictures?
what if we want to place them practically anywhere we want?
what if we want to tinker with its size, opacity and so on?
At the moment, TradingView doesn't have any public indicator that can meet all these needs... until we decided to change that today :)
So, without further ado, please welcome Pixel Canvas – an indicator, which allows you to turn ANY picture to high-quality pixel art on your chart. Whether it is PNG, JPG, JPEG, WEBP, or any other static image format, Pixel Canvas will help you place it on your chart with (almost) no effort.
This opens a bunch of cool opportunities:
Learning how to trade and need a cheatsheet always on the chart? No problem, add through Pixel Canvas and it will be always on your chart!
Want to always have a picture of your favourite pet when trading? Pixel Canvas can help with that!
Need to leave for a meeting at 5 PM, but can't skip the trading session? Put a reminder directly on your chart with Pixel Canvas!
And on, and on, and on – you get the idea :)
"Wait, but why make this indicator, if you can just paste a picture directly on TradingView? ", – you would ask.
The answer is simple : try panning your chart in any direction – your image will move with the chart, so you will constantly lose sight of your image and and, as of March 15th, 2026 , there is no way to lock your picture on the screen. Pixel Canvas makes your image stay "on top of the chart" , just like table objects do (spoiler alert: your pixel art in Pixel Canvas is, in fact, a table, but a HUGE one) .
Here are just a few examples of what you can have on your chart with Pixel Canvas:
(based on public image, freely available on Internet)
(based on public image, freely available on Internet)
(based on public image, freely available on Internet)
You can use PNGs and their transparent parts will be transparent on the chart as well! Here, take a look:
(based on public image, freely available on Internet)
(based on public image, freely available on Internet)
(based on public image, freely available on Internet)
Sounds interesting? Leap further then!
🖼️ HOW DOES IT WORK? (BASIC OVERVIEW)
Pixel Canvas takes, what we call, a colour matrix as its sole input. Colour matrix is a string, which consists of hexadecimal 6-digit colour codes for each pixel on your picture. Each colour code is separated by a comma symbol – `,`; each row of the colour codes is separated by semicolon `;`.
Such colour matrix is then parsed by the indicator, saving all the colour codes in one big array – colour code array . The size of this array is auto-determined based on the size of pasted colour matrix. For displaying the resulting picture we use table object, whose size equals to the size of pasted colour matrix, obviously. When colour matrix is parsed, colour codes from the resulting array are projected onto the respective cells of the table object. In the end, the indicator displays a big coloured table, which represents an exact pixel-for-pixel copy of the original picture!
The resulting quality is usually pretty cool (not perfect, it is called pixel art for a reason) , check the examples yourself:
(based on personal image)
(based on personal image)
(based on personal image)
All that looks cool enough, but how to actually create a colour matrix itself ?
There are 3 options for this (from easiest to hardest) :
Use our own pixel art web converter . It is a free web tool, which we built ourselves specifically for this indicator. It is very simple to use: you upload or paste an image, the tool automatically downscales it to the highest resolution that fits within TradingView's limits (we will mention them further in this description) , applies compression where possible, and outputs an optimized matrix string. You copy that string, paste it into the indicator — done .
Important: we do not store any images that you upload to this platform!
Each colour matrix calculation is performed client-side using simple JSX – nothing gets sent to our servers. We built everything with security in mind.
The link to the converter app is in our signature.
Complete and interactive usage guide is available near the top our web app !
Ask an LLM to help you. Since the matrix format is just text, you can technically ask any LLM (we won't mention specific ones to comply with TradingView's rules) to generate a colour matrix from an image. This works for very small and simple images (think a 5×5 smiley face), but be warned — LLMs struggle significantly with this task at any meaningful scale. They tend to hallucinate colours, lose track of row lengths, skip cells, and produce matrices that are either malformed or visually nothing like the original. If you go this route, expect to spend time debugging. For anything beyond a tiny sketch, the companion converter will save you a lot of frustration.
If you still want to try, here is a prompt you can copy, paste into your LLM along with the image, and get a result in the right format:
Convert this image into a colour matrix for TradingView's Pixel Canvas indicator.
Rules:
- Output ONLY the matrix string, nothing else.
- Rows are separated by semicolons (;). Cells within a row by commas (,).
- Each cell is a 6-character uppercase hex colour code WITHOUT the # symbol (e.g. FF0000 for red, 00FF00 for green).
- For transparent or empty pixels, leave the cell EMPTY (no text between commas). Example of a row with empty cells: FF0000,,,,00FF00
- If all 6 hex characters in a cell are identical (e.g. FFFFFF, 000000, AAAAAA), compress it to a single character (F, 0, A).
- Every row MUST have the exact same number of commas (same number of cells).
- Target resolution: no more than 100 columns and 100 rows (max 10,000 cells total).
- The entire string must be under 40,960 characters.
- Do NOT add any line breaks, code blocks, backticks, or explanations. Just the raw matrix string.
Build a converter yourself. We personally used a single JSX-script to make this converter, because it's the fastest way to deploy and use it on the web. The format of this converter is open and simple enough to construct manually or with your own script. Rows are separated by semicolons, cells within a row by commas. Each cell is either a 6-character hex colour code (like `FF0000` for red), an empty value for a transparent cell (just nothing between commas), or a single character for solid colours where all hex digits are the same (like `F` instead of `FFFFFF`).
Once you have a matrix string from any of these methods, paste it into the indicator's Color Matrix input field. The indicator parses it and renders the image on your chart.
⚙️ SETTINGS
We have managed to squeeze all the important stuff into a bunch of settings, which definitely won't overwhelm anyone. Let's quickly review the whole thing:
📐 Matrix
Color Matrix — the text area where you paste your colour matrix string.
📏 Size & Position
Picture Scale (%) — controls how large the image appears. 100% fills the available pane space. 50% is half size. The image always keeps its correct proportions regardless of scale.
Screen Ratio (W / H) — the width-to-height ratio of your chart pane. Default is 1.8 (close to 16:9). If the image looks stretched horizontally, increase this value. If stretched vertically, decrease it. Fine-tune in small steps.
Position — where the image sits on the chart. Nine positions available: any combination of Top / Middle / Bottom and Left / Center / Right.
🎨 Style
Show Grid Lines — draws a thin border around each pixel. Off by default.
Grid Color — colour of the grid lines when enabled.
Transparent Empty Cells — when ON, empty cells in the matrix are fully invisible. When OFF, they use the colour below.
Empty Cell Color — background colour for empty cells when transparency is off. Match this to your chart background for a clean look.
🛠 THE ENGINEERING BEHIND
Now let's talk about the system under the hood and its technical design in depth. We will start from explaining what rules colour matrix truly abides by.
1. COLOUR MATRICES
As it was stated in the beginning of section "HOW DOES IT WORK (BASIC OVERVIEW)?" , colour matrix is a string, where rows are separated by semicolons and individual colour codes within a row are separated by commas. Each colour code is one of three things:
6-character hex colour code (e.g. `FF0000` for red),
an empty value for a transparent cell (nothing between commas)
or a single character that represents a solid colour where all six hex digits are the same (e.g. `F` instead of `FFFFFF`)
That last format is a compression trick — the companion converter detects when a pixel's red, green and blue channels are identical and stores it as one character instead of six. On a typical image with a dark or light background, this alone can save thousands of characters.
The matrix dimensions are auto-detected. Pixel Canvas counts semicolons to determine the number of rows, then splits the first row by commas to determine the number of columns. A validation pass checks that every row contains the same number of columns.
If there is a mismatch, the indicator displays an error banner instead of rendering. Each error points out an inconsistency with its coordinates in the matrix.
See error example on the screenshot below:
Now let's dive into how the indicator creates colours from the obtained colour codes.
2. HEX PARSING
Pine Script does not have a native function to convert hex strings into colours. We had to build a character-level parser from scratch. From each parsed colour code, which is in RRGGBB format, we take every character (`0`–`F`) one by one and map it to its corresponding integer code, ranging from 0–15 (because there are 16 possible variations) , through a `switch` statement. After assigning each character its corresponding integer value, the parser pairs them into three bytes (red, green, blue), calculates each byte's value using standard equation X * 16 + Y (where X - int code of 1st element of the pair, Y - int code of 2nd element of the pair) and calls `color.rgb()` to produce the final colour.
For example, code `FFAD00` will transform into RGB like this:
`FFAD00` =>
assign int code to each character: `0` = 0, `1` = 1, ... , `a`/`A`= 10, ... , `f`/`F` = 15)
=> `F` = 15, `F` = 15, `A` = 10, `D` = 13, `0` = 0, `0` = 0
`FFAD00` => `FF` + `AD` + `00` – 3 pairs;
`FF` => X * 16 + Y, X = code of `F`, Y = code of `F` => 15 * 16 + 15 = 255;
`AD` => X * 16 + Y, X = code of `A`, Y = code of `D` => 10 * 16 + 13 = 173;
`00` => X * 16 + Y, X = code of `0`, Y = code of `0` => 0 * 16 + 0 = 0;
Resulting RGB colour = color.rgb(`FF`, `AD`, `00`) => color.rgb(255, 173, 0) ;
See visual explanation on the screenshot below:
For the single-character shorthand, the parser detects that the cell has length 1, reads the character once, and constructs a greyscale colour where R = G = B. For example, colour code `F` is the same as code `FFFFFF` and will be treated by the parser as such. This avoids expanding the character back into six copies and parsing them separately.
The branching logic inside `parseCell()` is ordered by string length. The most common path — a 6-character hex code — is checked early, before falling through to less common formats. This matters because the function runs for every cell in the matrix, and for a large image that can be up to 10,000 calls.
That's it for hex colour code parsing. Now for one of the most interesting questions – how we manage to preserve the aspect ratio of the original image.
3. ASPECT RATIO PRESERVATION
TradingView table cells accept width and height as percentages of the indicator pane's horizontal and vertical space, respectively. The problem is that these two axes refer to different physical dimensions on screen. If you set both width and height to the same percentage value, the cells will not appear square — they will be stretched, because chart panes are, on average, rectangular — wider than they are tall.
To compensate for this and make resulting pixel arts look almost the same as the original, the indicator uses a screen ratio parameter — the approximate width-to-height ratio of your chart pane. The formula is straightforward: for a cell to appear visually square, its height percentage must equal its width percentage multiplied by the screen ratio. The indicator then fits the entire canvas within 80% of the pane on both axes by computing the maximum width that satisfies both constraints and deriving the height from it.
The Picture Scale input acts as a simple multiplier on top of this base calculation. At 100% the image fills the available pane space. At 50% it renders at half size. The aspect ratio stays locked regardless of the scale value.
See the differences in size of the same picture with different Picture Scale parameter values on the screenshot below:
Alright, but what about performance?
4. PERFORMANCE
Let's be upfront: if you load a large matrix (5,000+ cells), the chart will feel less responsive when you scroll, pan or zoom. This is not a bug in the indicator and unfortunately not something we can fix from the PineScript side. Here is why it happens and what we have done about it.
TradingView's table objects were designed for compact data panels — small boxes with 10–30 cells showing indicator values, dashboards, that kind of thing. They were not built with the expectation that someone would fill a table with thousands of individually coloured cells and use it as a pixel grid. But that is exactly what this indicator does. The issue is that every time the chart viewport changes — a pan, a scroll, a zoom — the platform's rendering engine repaints all table cells from scratch. At 8,000–10,000 cells, each repaint takes a noticeable amount of time. This is a platform-level rendering characteristic that no amount of PineScript optimization can change.
What we can control is how fast the indicator itself executes, and we have pushed that to the technical limit. The `indicator()` declaration uses `calc_bars_count = 1`, which tells the Pine runtime to load exactly one bar instead of the usual 5,000–20,000+. Since Pixel Canvas does not use any chart data — no `close`, no `volume`, no moving averages — there is no reason to process historical bars at all. The script runs once, builds the table, and never re-executes. On top of this, values that do not change between cells (like the background colour for empty cells) are pre-computed before the render loop, and all unused drawing max counts (`max_labels_count`, `max_boxes_count`, `max_lines_count`) are set to min value to free up resources.
In short: the indicator code itself runs as fast as PineScript allows . The lag you experience after that is the platform's table renderer doing its best with a workload it was never designed for.
A few tips that will help with responsiveness:
Use a lower resolution image when possible. Fewer cells = less work for the renderer on each redraw. The companion converter's Custom mode lets you set a specific target row count if Auto produces a matrix that feels too heavy.
If you only need the image for a screenshot or a static chart, the lag does not matter — the image renders correctly regardless of how responsive the chart feels during interaction.
Keep heavy indicators off the chart while Pixel Canvas is active, if possible. This frees up rendering resources, especially on slower machines.
Avoid rapid zooming, because each zoom level change triggers a full table repaint.
OK, performance is covered, but how far can we actually go with image quality?
Well, we are kinda limited in that regard and it is not our fault. Let us explain why.
5. TRADINGVIEW LIMITS
During the process of development and playing with colour matrices of different sizes, we have stumbled upon a few hard-coded limitations of TradingView, that limit the maximum size of the colour matrix, and thus the quality of the resulting picture.
These limits are:
`input.text_area()` function – the input field for your colour matrix string – accepts a maximum of 40,960 characters .
A single table object supports a maximum of approximately 10,000 cells .
Both of these are enforced by TradingView on the PineScript level and cannot be bypassed.
Despite those limits, some might argue that we could have achieved better pixel art quality by using multiple tables instead of just one and drawing different parts of a high-quality pixel art on separate tables.
That is a good idea, but it comes with a catch: PineScript v6 tables have only 9 possible positions on the chart. This simply doesn't allow us to split a pixel art across tables, because there is no way to properly align and connect them into one seamless image.
Our free pixel art web converter (linked in our signature field) is designed with these constraints in mind. By default our converter builds your pixel art with the highest possible resolution that fits within TradingView limits.
Also, to save on the colour matrix size and add more cells to improve the resulting art's quality, our converter detects all colour codes that can be saved as a single character.
As an example, an absolutely white pixel with code `FFFFFF` can be saved as a single character `F`. This is a game-changer feature for cases when you want to use a picture with a monotone background, because this compression technique will significantly reduce the number of characters in the resulting colour matrix, thus allowing for more cells, which simply means better image quality.
See comparison of the results from average LLM-generated colour matrix and our converter's colour matrix for the same picture (for moderators: this is not an attempt for promotion; it is intended only for comparison purposes within description's context) :
(based on personal image)
That's it for the engineering part!
💡 TIPS
If the image looks stretched, adjust Screen Ratio first. Most widescreen monitors work well with values between 1.6 and 2.0.
Use the Auto mode in the companion converter for the best results. It will find the highest resolution that fits within both limits automatically.
For images with transparent backgrounds (like logos), make sure Transparent Empty Cells is enabled in the indicator settings. This way the background of the image blends with the chart.
If you want a solid background behind the image instead, disable transparency and set Empty Cell Color to your preferred colour.
Images with fewer unique colours compress better. Pixel art, flat illustrations, and logos tend to produce smaller matrices than photographs.
🏁 AFTERWORD
We have had a lot of fun building Pixel Canvas and figuring out the best possible ways to draw an image as a colour matrix with the highest possible quality AND achieving the best possible performance at the same time, while exploring and adapting the whole thing to TradingView's technical limitations.
As of now, on March 15th, 2026, we believe that we have pushed the engineering part of this indicator to its limits, achieving the best possible performance (if not, please prove us wrong in the comments) , but nevertheless we will continue to monitor the updates for PineScript and who knows – maybe in PineScript v7 we will be able to optimize Pixel Canvas completely to zero lag :)
It would be a joy to us if anyone finds our technical solutions for this indicator interesting and adapt it to their needs, so, to anyone who is passionate about such things: feel free to iterate on our work as much as you want.
In short, we just wanted to build something cool, fun and optimized and it just so happens that it also is truly valuable to share with the community due of the absence of such tools in the open-source space.
We sincerely hope that this simple script will make your day a bit better with the picture of your pet, remind you to take that break between trading sessions and lift your mood with a meme 🤗
Best regards,
— WinWorld Team
אינדיקטור Pine Script®

















