I've been beating my head against this for a while. The reason the rings are not aligned is because I initially started the project in Photoshop.

How can I the change the distance between rings to be a uniform measurement?

This is a close-up of the rings. Right now you can see that some are closer than others.

enter image description here

This is a photo of the full image:

enter image description here

  • 3
    One thing isn't clear to me: The circles have different stroke width. Do you need the same distance between the middle of each stroke or do you need the same white space between each circle. The last one is even more difficult since the diameter must take the stroke width of each circle into account.
    – Wolff
    Aug 21, 2019 at 17:16

4 Answers 4


I'd say things like this are better done with automation. Imagine width of one of the items in the middle of your collection changes and you'll have to rebuild half of the circles. Or you need to make all the circles 50% more wide. Oh man!

I don't know Illustrator scripting, but in Photoshop it's quite straightforward. Basically I have a data array with properties like name, value, color and I have a function that creates a shape layer in specific coordinates with a specific radius. Plus I have several additional parameters like a gap between the circles, a maximum width of a circle and a starting radius.

Using this data, in a loop I create a circle for each data entry. Center is always a document center, width is calculated linearly based on a maximum width and a maximum data value found, radius is calculated based on a width, previous radius and a gap.

Here're different results based on different settings for a gap, starting radius and max width:

enter image description here

And the code I used:

function main()

    //data definitions
    var center = {
        x: activeDocument.width.as("px") / 2,
        y: activeDocument.height.as("px") / 2
    var gap = 3; // gap between circles
    var startRad = 200; // starting radius
    var maxWidth = 15; // maximum width
    var maxValue = -1; // stores maximum value found
    var width, i;

    var data = [
        color: [223,41,53],
        value: 22,
        name: "Jerelyn Pinkbeard"
        color: [65,39,34],
        value: 13,
        name: "Spiteful Christine Black"
        color: [223,160,110],
        value: 44,
        name: "Generous Christi"
        color: [12,243,187],
        value: 4,
        name: "Scrawny Tiphanie"
        color: [134,186,144],
        value: 12,
        name: "Cutthroat Deena The Spiteful"
        color: [5,47,95],
        value: 22,
        name: "Jerilyn Blackhate"
        color: [0,83,119],
        value: 60,
        name: "Tiphanie Generousparrot"
        color: [6,167,125],
        value: 33,
        name: "First Mate Christa The Scrawny"
        color: [213,198,122],
        value: 12,
        name: "Dread Pirate Geraldine"

    // START

    // first calculating maximum value of all data properties to use it later for linear transforms
    for (i = 0; i < data.length; i++)
        if (data[i].value > maxValue) maxValue = data[i].value;

    // creating circles
    for (i = 0; i < data.length; i++)
        //calculating width lineary based on maximum value found and maximum width possible
        width = linear(data[i].value, 0, maxValue, 0, maxWidth);

            x: center.x, // X center of the document
            y: center.y, // Y center of the document
            color: data[i].color, // color from my data
            width: width, // calculated width
            radius: startRad, // starting radius
            name: data[i].name // and a name

        startRad += width + gap; // updating starting radius with width and gap

    function createCircle(data)
        var desc2 = new ActionDescriptor();
        var ref1 = new ActionReference();
        desc2.putReference(cTID('null'), ref1);
        var desc3 = new ActionDescriptor();
        var desc4 = new ActionDescriptor();
        var desc5 = new ActionDescriptor();
        desc5.putDouble(cTID('Rd  '), 45.000001);
        desc5.putDouble(cTID('Grn '), 77.000003);
        desc5.putDouble(cTID('Bl  '), 113.000001);
        desc4.putObject(cTID('Clr '), cTID('RGBC'), desc5);
        desc3.putObject(cTID('Type'), sTID('solidColorLayer'), desc4);
        var desc6 = new ActionDescriptor();
        desc6.putInteger(sTID('unitValueQuadVersion'), 1);
        desc6.putUnitDouble(cTID('Top '), cTID('#Pxl'), data.y - data.radius);
        desc6.putUnitDouble(cTID('Left'), cTID('#Pxl'), data.x - data.radius);
        desc6.putUnitDouble(cTID('Btom'), cTID('#Pxl'), data.y + data.radius);
        desc6.putUnitDouble(cTID('Rght'), cTID('#Pxl'), data.x + data.radius);
        desc3.putObject(cTID('Shp '), cTID('Elps'), desc6);
        var desc7 = new ActionDescriptor();
        desc7.putInteger(sTID('strokeStyleVersion'), 2);
        desc7.putBoolean(sTID('strokeEnabled'), true);
        desc7.putBoolean(sTID('fillEnabled'), false);
        desc7.putUnitDouble(sTID('strokeStyleLineWidth'), cTID('#Pxl'), data.width);
        desc7.putUnitDouble(sTID('strokeStyleLineDashOffset'), cTID('#Pnt'), 0.000000);
        desc7.putDouble(sTID('strokeStyleMiterLimit'), 100.000000);
        desc7.putEnumerated(sTID('strokeStyleLineCapType'), sTID('strokeStyleLineCapType'), sTID('strokeStyleButtCap'));
        desc7.putEnumerated(sTID('strokeStyleLineJoinType'), sTID('strokeStyleLineJoinType'), sTID('strokeStyleMiterJoin'));
        desc7.putEnumerated(sTID('strokeStyleLineAlignment'), sTID('strokeStyleLineAlignment'), sTID('strokeStyleAlignOutside'));
        desc7.putBoolean(sTID('strokeStyleScaleLock'), false);
        desc7.putBoolean(sTID('strokeStyleStrokeAdjust'), false);
        var list1 = new ActionList();
        desc7.putList(sTID('strokeStyleLineDashSet'), list1);
        desc7.putEnumerated(sTID('strokeStyleBlendMode'), cTID('BlnM'), cTID('Nrml'));
        desc7.putUnitDouble(sTID('strokeStyleOpacity'), cTID('#Prc'), 100.000000);
        var desc8 = new ActionDescriptor();
        var desc9 = new ActionDescriptor();
        desc9.putDouble(cTID('Rd  '), data.color[0]);
        desc9.putDouble(cTID('Grn '), data.color[1]);
        desc9.putDouble(cTID('Bl  '), data.color[2]);
        desc8.putObject(cTID('Clr '), cTID('RGBC'), desc9);
        desc7.putObject(sTID('strokeStyleContent'), sTID('solidColorLayer'), desc8);
        desc7.putDouble(sTID('strokeStyleResolution'), 72.000000);
        desc3.putObject(sTID('strokeStyle'), sTID('strokeStyle'), desc7);
        desc2.putObject(cTID('Usng'), sTID('contentLayer'), desc3);
        desc2.putInteger(cTID('LyrI'), 2);
        executeAction(cTID('Mk  '), desc2, DialogModes.NO);

        if (data.name != undefined)
            var desc = new ActionDescriptor();
            var ref = new ActionReference();
            ref.putEnumerated(cTID("Lyr "), cTID("Ordn"), cTID("Trgt"));
            desc.putReference(cTID('null'), ref);
            var descName = new ActionDescriptor();
            descName.putString(cTID('Nm  '), data.name);
            desc.putObject(cTID('T   '), cTID('Lyr '), descName);
            executeAction(cTID('setd'), desc, DialogModes.NO);
    } // end of createCircle()

    function linear(X, A, B, C, D)
        var Y = (X - A) / (B - A) * (D - C) + C
        return Y;

    function cTID(s)
        return app.charIDToTypeID(s);

    function sTID(s)
        return app.stringIDToTypeID(s);

app.activeDocument.suspendHistory("create circles", "main()");
  • Nice solution +1, although like the other answers, this would mean the OP would still have to start all over again, Also, wouldn't it be possible to perhaps link this script to read data from an Excel spread sheet rather than type it again?
    – Billy Kerr
    Aug 21, 2019 at 9:13
  • 2
    It'd be possible to read data from a csv-file exported from Excel for example, if OP updates his answer with a sample csv I can include this step. And yes, this actually answers a different question (how to approach a task like that) rather than OP's question, but as others mentioned, I don't think it's possible to fix the existing project. Good news the script can create a new layout in seconds :) Aug 21, 2019 at 9:23
  • TIL Illustrator can be scripted with JS, AppleScript or VB Script.
    – aloisdg
    Aug 21, 2019 at 11:32
  • Instead of reading from Excel, use formulas to generate the code, e.g. ="{ color:[" & A1 & ", " & A2 ...
    – Chris
    Aug 21, 2019 at 16:54
  • Where this A1 would come from? Aug 21, 2019 at 19:52

It's unfortunate that you hadn't begun your project in Illustrator. Photoshop just isn't the right kind of software for work like this since the output would be raster, not vector. I suppose you could start over again. As Scott has said, short of finding (or writing) a script, neither Illustrator nor Photoshop has any automated way of scaling concentric circles of various widths so that the gap between them is the same, after the fact.

In Illustrator it would have been easy enough to create a polar grid, and fill the cells with the Live Paint bucket.

For example:

enter image description here

  • Select all

  • Align on center vertically

  • Align on center horizontally

Of course this won't alter the diameter of the circles.

If you want equidistant between each circle, then you need to use diameters that are the proper sizes.

In that case.... it may honestly be easier to start over. See here.

  • 1
    I have tried to use the Align function but you're right, it doesn't create a uniform gap between circles. I have been working on this project for 3 weeks and each ring is correlated with financial data to derive its thickness. Is there no way to work with what I already have created? It would be quite the task to restart Aug 21, 2019 at 0:33
  • 1
    Well it would take as much if not more work to work with what you have because you'd have to alter the diameter of each and every ring to the proper measurement to create equal spacing. I don't know of anything in Illustrator that will "auto-size" circles in a concentric fashion. You might be able to find a script somewhere for that - but again, time searching for it may be more time that it takes to simply start over.
    – Scott
    Aug 21, 2019 at 0:38
  • Thanks Scott. I appreciate your help. Any place you suggest that I might be able to find the script? Aug 21, 2019 at 1:03
  • 1
    Is this kind of display an effective communicator of the presented information? There are a lot of circles there and it's not apparent to me that the users who are being presented with this data will be able to quickly glean the desired understanding of the financial data and relationships.
    – sintax
    Aug 21, 2019 at 20:53

Restart in Illustrator. Make an art brush which contains a rectangular piece of each ring. Apply the brush to a circle stroke:

enter image description here

I drew 5 rectangles. Their widths = the wanted ring widths. They were selected all and dragged to the brushes collection. In the definition dialog I selected "Art Brush". See the definition dialog:

enter image description here

I drew a circle with diameter = the average diameter of the rings and applied the new brush to it. It's in the left in the first attached image.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.