I've found this rad looking halftone shader but as I'm pretty new to blender I don't know how to implement it. I've put it in the text editor then tried to get a script node to run it but it hits an OSL error.

Here is where I found the code https://blenderartists.org/forum/showthread.php?125741-PyNode-cookbook/page8&p=1234519#post1234519

Here is the code itself:

from Blender import Node
import math

sqrt_2 = math.sqrt(2)

class DotsNode(Node.Scripted):
    def __init__(self, sockets):
        radius = Node.Socket('radius', val=4*[1.0])
        grid = Node.Socket('grid', val=10.0, min=1.0, max=100.0)
        fg_color = Node.Socket('fg_color', val=3*[0.0]+[1.0])
        bg_color = Node.Socket('bg_color', val=4*[1.0])
        color = Node.Socket('color', val=4*[1.0])
        sockets.input = [radius, grid, fg_color, bg_color]
        sockets.output = [color]

    def nearest(self, coords, grid):
        """Return the nearest grid coordinate to coords."""
        x, y = coords
        gx, mx = divmod(x, grid)
        gy, my = divmod(y, grid)

        x = gx * grid
        y = gy * grid

        if gx % 2 == gy % 2:
            if grid - mx > my:
                return x, y
                return x+grid, y+grid
            if mx > my:
                return x+grid, y
                return x, y+grid

    def __call__(self):
        input = self.input
        coords = [self.shi.pixel[0], self.shi.pixel[1]]
        grid = input.grid # pixels
        radius = (1.0 - input.radius[0]) * grid

        # Get the square distance from coords to nearest grid point
        nearest = self.nearest(coords, int(grid / sqrt_2))
        sqdist = (coords[0]-nearest[0])**2 + (coords[1]-nearest[1])**2      

        # if sqdist is less than the scaled radius squared than color one
        if sqdist < radius**2:
            self.output.color = self.input.fg_color
            self.output.color = self.input.bg_color

__node__ = DotsNode

Here is a sample image from the original post :

example image

How can I produce this effect?

  • $\begingroup$ With the code selected press the Code Sample button in the tool bar, or press ctrl + K . More info here. $\endgroup$
    – David
    Jan 26, 2018 at 0:32
  • $\begingroup$ Thanks David that will definitely help with any further questions I have! $\endgroup$
    – aokayprice
    Jan 26, 2018 at 2:06
  • $\begingroup$ Hello @aokayprice :). Could you please mark the answer as accepted, if it solved your problem? Thanks :). $\endgroup$ Mar 29, 2021 at 18:25

1 Answer 1


The post you reference in your question is almost 10 years old and appears to rely on Blender features and APIs that are no longer available.

The code is definitely not for an OSL shader - so using a Script node will not work - it's actually a python script.

The first 'import' statement attempts to import 'Node' from package 'Blender' - as far as I understand it, this was the old package for API calls to interface with Blender that has since been replaced with package 'bpy' (blender python).

It is possible to create such an effect in the compositor using a similar technique to that apparently used in the original script.

To achieve this in the compositor we need to be able to determine the location of each pixel in the image so that we can determine how far each pixel is from the a grid used to position each of the halftone circles, scaling the circles based on the brightness at that point. The pixel location isn't directly availabel to the compositor nodes but we can use Blend Textures, which vary across the image, and feed those into the compositor to derive the X and Y location.

Start by swapping to the Texture properties panel and click 'New' to create a new texture. Give it a meaningful name (such as 'HorizontalBlend', set the Type to 'Blend' and set the direction to Horizontal. Ensure the Progression is set to Linear.


Repeat for 'VerticalBlend' (click the '+' to add a new texture), setting the direction of this one to Vertical.


These textures can be accessed in the compositor to determine the X and Y location in the image.

In order to create the halftone pattern we need to be able to split the image into a series of grid cells. Each cell will represent one 'blob' of the halftone pattern whose size will depend on the brightness of the rendered image at that point.

The following nodes can be used to generate an array of 'blobs' that we can use for the halftone :

nodes for blobs

This will produce something like the following :


The position in the image is split into a series of bands by the Modulo function (smaller values produce narrower bands). This is then multiplied up to the range of 0.0 to 2.0, 1 is subtracted (so the range is -1.0 to 1.0) and the Absolute node reverses the sign of the negative values. This results in a value that starts at 1.0, reduces to 0 at the grid line, and increases back to 1 - ie, the distance from the grid line. The same is done in the other dimension and the results combined (with pythagoras - a^2 + b^2 = c^2) to give the distance from the centre of each circle.

We can turn this into the halftone image by simply comparing the distance to the nearest centre with the brightness at the current point in the image. This produces the following rudimentary halftone :

basic halftoine results basic halftone original

basic halftone nodes

To refine this a bit we can slightly change the pattern by offsetting each alternate row - this will produce a hexagonal arrangement of blobs that produces a more pleasing result :

hex halftone nodes

hex halftone results

Adding Mix nodes we can add a splash of colour for the two halftone shades and the background (set to black by setting Environment Ray Visibility to disable visibility to the camera) and the foreground (drawn using Freestyle as white lines) and nodes to allow fine tuning of the halftone (the Divide and Subtract nodes).

tune halftone and adjust colours

tuned halftone and colours

To finish this off we can add nodes to allow the horizontal and vertical dot pitch and better adjustment for the colours, brightness and contrast as well as Blur nodes to allow the effect to be softened (blurring before the halftone removes hard edges and fine detail that would interfere with the pattern, whereas the blur after the halftone softens the 'blobs' themselves) to produce the following nodes :

final material

By unchecking the camera visibility of the environment (so it appears black) and by adding pure white edges using Freestyle we can achieve the following result :

final render

Blend file included

EDIT I've extended this to produce a colour halftone by simply splitting the image channels, duplicating the nodes, and re-combining the result. You could potentially change the dot pitch on each channel for different halftone effects.

Here's the node tree (apologies for the scale but it's included in the blend below) :

colour halftone nodes

The coloured original :

coloured original

And the coloured halftone :

coloured halftone

Blend file included

You can also experiment with the dot pitch to use different pitch per channel. For example :

coloured - different pitch

  • $\begingroup$ Thanks rich I will try to figure out how to ask my question and then post something! $\endgroup$
    – aokayprice
    Jan 26, 2018 at 19:15
  • $\begingroup$ @aokayprice Your question got me interested in producing halftone effects in the compositor. Please see the edit. Hope this helps. $\endgroup$ Jan 31, 2018 at 22:40
  • 1
    $\begingroup$ Wow rich this is incredible! Thank you so much for the hard work that must have gone into making this node structure. I'd been obsessing about it too and found some other much less robust solutions that I posted on my reddit thread here (reddit.com/r/blender/comments/7t06w3/halftones_in_blender) I will post a link there of your solution too for anyone interested. $\endgroup$
    – aokayprice
    Feb 1, 2018 at 23:02
  • $\begingroup$ Thanks @aokayprice - I've really enjoyed working on this. I've extended it to produce a coloured halftone - please see the edit. I think this could produce quite interesting effects, particularly with different dot pitches for the different channels. $\endgroup$ Feb 2, 2018 at 8:08
  • 1
    $\begingroup$ Wow it keeps growing! Thanks for the blend file it would take me a lil while to reconstruct it for sure. Definitely very interesting results, I have a lot of friends working in 2D animation / indie comics and I'm really excited to bring some of that aesthetic to 3D animation. I'm still learning the basics but they're going to be jealous when I can make a cartoon without drawing a million frames. $\endgroup$
    – aokayprice
    Feb 2, 2018 at 20:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .