CGShading lets you do color ramps. Setting one up is a little complicated, juggling a couple of CG data structures and writing the ramping function.float domain[2] = { 0.0, 1.0 }; // 1-in function float range[8] = { 0.0, 1.0, // N-out, RGBA 0.0, 1.0, 0.0, 1.0, 0.0, 1.0 }; CGFunctionCallbacks callbacks = { 0, evaluate, NULL }; CGFunctionRef shaderFunction; shaderFunction = CGFunctionCreate (self, // info / rock / context 1, // # of inputs for domain domain, // domain 4, // # of inputs for range range, // range &callbacks); CGColorSpaceRef deviceRGB; deviceRGB = CGColorSpaceCreateDeviceRGB (); CGShadingRef shader; shader = CGShadingCreateAxial (deviceRGB, // colorspace cgpoint(start), // start of axis cgpoint(end), // end of axis shaderFunction, // shader, 1-n, n-out NO, // extend start NO); // extend end CGContextSaveGState (context); { NSRect bounds = [self bounds]; CGContextClipToRect (context, cgrect(bounds)); CGContextDrawShading (context, shader); } CGContextRestoreGState (context); [self drawSpotAt: start size: 4]; [self drawSpotAt: end size: 4]; CGFunctionRelease (shaderFunction); CGColorSpaceRelease (deviceRGB); CGShadingRelease (shader);And the evaluator function is given an array of inputs and outputs. Use the in value(s) (which run from your domain's start-to-finish values) to generate the out values (which should be in the range's start-to-finish values):static void evaluate (void *info, const float *in, float *out) { float thing; thing = in[0]; out[0] = thing; out[1] = thing; out[2] = thing; out[3] = 1.0; } // evaluate